<!DOCTYPE HTML>
<html lang="zh-CN">


<head>
    <meta charset="utf-8">
    <meta name="keywords" content="SpringBoot&amp;SpringCloud, 博客 blog">
    <meta name="description" content="熊猫小二的博客  xmxe&#39;s blog">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <!-- 为了引用qq空间图床文件 -->
    <meta name="referrer" content="no-referrer">
    <!-- Global site tag (gtag.js) - Google Analytics -->


    <title>SpringBoot&amp;SpringCloud | 熊猫小二</title>
    <link rel="icon" type="image/x-icon, image/vnd.microsoft.icon" href="/blog/favicon.ico">
    <link rel="stylesheet" type="text/css" href="/blog/libs/awesome/css/all.css">
    <link rel="stylesheet" type="text/css" href="/blog/libs/materialize/materialize.min.css">
    <link rel="stylesheet" type="text/css" href="/blog/libs/aos/aos.css">
    <link rel="stylesheet" type="text/css" href="/blog/libs/animate/animate.min.css">
    <link rel="stylesheet" type="text/css" href="/blog/libs/lightGallery/css/lightgallery.min.css">
    <link rel="stylesheet" type="text/css" href="/blog/css/matery.css">
    <link rel="stylesheet" type="text/css" href="/blog/css/my.css">
    <link rel="stylesheet" type="text/css" href="/blog/css/loading.css">

    <script src="/blog/libs/jquery/jquery.min.js"></script>

<meta name="generator" content="Hexo 6.3.0"></head>



   
<style>
    body{
       background-image: url(/blog/medias/cover.jpg);
       background-repeat:no-repeat;
       background-size:cover;
       background-attachment:fixed;
    }
</style>



<body>
    
  <div id="loading-box">
    <div class="loading-left-bg"></div>
    <div class="loading-right-bg"></div>
    <div class="spinner-box">
      <div class="configure-border-1">
        <div class="configure-core"></div>
      </div>
      <div class="configure-border-2">
        <div class="configure-core"></div>
      </div>
      <div class="loading-word">加载中...</div>
    </div>
  </div>
  <!-- 页面加载动画 -->
  <script>
    $(document).ready(function () {
      // document.body.style.overflow = 'auto';
      document.getElementById('loading-box').classList.add("loaded")
    })
  </script>

    <header class="navbar-fixed">
    <nav id="headNav" class="bg-color nav-transparent">
        <div id="navContainer" class="nav-wrapper container">
            <div class="brand-logo">
                <a href="/blog/" class="waves-effect waves-light">
                    
                        <img src="/blog/medias/logo.png" class="logo-img" alt="LOGO">
                    
                    <span class="logo-span">熊猫小二</span>
                </a>
            </div>
            

<a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a>
<ul class="right nav-menu">
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/blog/" class="waves-effect waves-light">
      
      <i class="fas fa-home" style="zoom: 0.6;"></i>
      
      <span>首页</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="#" class="waves-effect waves-light">

      
      <i class="fas fa-book" style="zoom: 0.6;"></i>
      
      <span>归档</span>
      <i class="fas fa-chevron-down" aria-hidden="true" style="zoom: 0.6;"></i>
    </a>
    <ul class="sub-nav menus_item_child ">
      
      <li>
        <a href="/blog/archives">
          
          <i class="fas fa-archive" style="margin-top: -20px; zoom: 0.6;"></i>
          
          <span>归档</span>
        </a>
      </li>
      
      <li>
        <a href="/blog/tags">
          
          <i class="fas fa-tags" style="margin-top: -20px; zoom: 0.6;"></i>
          
          <span>标签</span>
        </a>
      </li>
      
      <li>
        <a href="/blog/categories">
          
          <i class="fas fa-bookmark" style="margin-top: -20px; zoom: 0.6;"></i>
          
          <span>分类</span>
        </a>
      </li>
      
    </ul>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/blog/friends" class="waves-effect waves-light">
      
      <i class="fas fa-address-book" style="zoom: 0.6;"></i>
      
      <span>友链</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="#" class="waves-effect waves-light">

      
      <i class="fas fa-user-circle" style="zoom: 0.6;"></i>
      
      <span>关于</span>
      <i class="fas fa-chevron-down" aria-hidden="true" style="zoom: 0.6;"></i>
    </a>
    <ul class="sub-nav menus_item_child ">
      
      <li>
        <a href="/blog/about">
          
          <i class="fas fa-star-of-david" style="margin-top: -20px; zoom: 0.6;"></i>
          
          <span>主页</span>
        </a>
      </li>
      
      <li>
        <a href="/blog/gallery">
          
          <i class="fas fa-images" style="margin-top: -20px; zoom: 0.6;"></i>
          
          <span>相册</span>
        </a>
      </li>
      
    </ul>
    
  </li>
  
  <li>
    <a href="#searchModal" class="modal-trigger waves-effect waves-light">
      <i id="searchIcon" class="fas fa-search" title="搜索" style="zoom: 0.85;"></i>
    </a>
  </li>
  
    <li>
      <a class="waves-effect waves-light" onclick="switchNightMode()">
        <i id="sum-moon-icon" class="fas fa-sun" style="zoom:0.65;" title="切换主题"></i>
      </a>
    </li>
  
  
</ul>


<div id="mobile-nav" class="side-nav sidenav">

    <div class="mobile-head bg-color">
        
          <img src="/blog/medias/logo.png" class="logo-img circle responsive-img">
        
        <div class="logo-name">熊猫小二</div>
        <div class="logo-desc">
            
            熊猫小二的博客  xmxe&#39;s blog
            
        </div>
    </div>

    

    <ul class="menu-list mobile-menu-list">
        
        <li class="m-nav-item">
	  
		<a href="/blog/" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-home"></i>
			
			首页
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="javascript:;">
			
				<i class="fa-fw fas fa-book"></i>
			
			归档
			<span class="m-icon"><i class="fas fa-chevron-right"></i></span>
		</a>
            <ul  >
              
                <li>

                  <a href="/blog/archives " style="margin-left:75px">
				  
				   <i class="fa fas fa-archive" style="position: absolute;left:50px" ></i>
			      
		          <span>归档</span>
                  </a>
                </li>
              
                <li>

                  <a href="/blog/tags " style="margin-left:75px">
				  
				   <i class="fa fas fa-tags" style="position: absolute;left:50px" ></i>
			      
		          <span>标签</span>
                  </a>
                </li>
              
                <li>

                  <a href="/blog/categories " style="margin-left:75px">
				  
				   <i class="fa fas fa-bookmark" style="position: absolute;left:50px" ></i>
			      
		          <span>分类</span>
                  </a>
                </li>
              
            </ul>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/blog/friends" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-address-book"></i>
			
			友链
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="javascript:;">
			
				<i class="fa-fw fas fa-user-circle"></i>
			
			关于
			<span class="m-icon"><i class="fas fa-chevron-right"></i></span>
		</a>
            <ul  >
              
                <li>

                  <a href="/blog/about " style="margin-left:75px">
				  
				   <i class="fa fas fa-star-of-david" style="position: absolute;left:50px" ></i>
			      
		          <span>主页</span>
                  </a>
                </li>
              
                <li>

                  <a href="/blog/gallery " style="margin-left:75px">
				  
				   <i class="fa fas fa-images" style="position: absolute;left:50px" ></i>
			      
		          <span>相册</span>
                  </a>
                </li>
              
            </ul>
          
        </li>
        
        
    </ul>
</div>


        </div>

        
    </nav>

</header>

    
<script src="/blog/libs/cryptojs/crypto-js.min.js"></script>
<script>
    (function() {
        let pwd = '';
        if (pwd && pwd.length > 0) {
            if (pwd !== CryptoJS.SHA256(prompt('请输入访问本文章的密码')).toString(CryptoJS.enc.Hex)) {
                alert('密码错误，将返回主页！');
                location.href = '/blog/';
            }
        }
    })();
</script>




<div class="bg-cover pd-header post-cover" style="background-image: url('/blog/medias/featureimages/8.jpg')">
    <div class="container" style="right: 0px;left: 0px;">
        <div class="row">
            <div class="col s12 m12 l12">
                <div class="brand">
                    <h1 id="post-title" class="description center-align post-title"></h1>

                    
                        <!-- <script src="https://cdn.jsdelivr.net/npm/typed.js@2.0.11"></script> -->
                        <script>
                            var typedObj = new Typed("#post-title", {
                                strings: [ 'SpringBoot&amp;SpringCloud' ],
                                startDelay: 300,
                                typeSpeed: 70,
                                loop: false,
                                backSpeed: 50,
                                showCursor: true
                            });
                        </script>
                    
                </div>
            </div>
        </div>
    </div>
</div>




<main class="post-container content">

    
    <link rel="stylesheet" href="/blog/libs/tocbot/tocbot.css">
<style>
    #articleContent h1::before,
    #articleContent h2::before,
    #articleContent h3::before,
    #articleContent h4::before,
    #articleContent h5::before,
    #articleContent h6::before {
        display: block;
        content: " ";
        height: 100px;
        margin-top: -100px;
        visibility: hidden;
    }

    #articleContent :focus {
        outline: none;
    }

    .toc-fixed {
        position: fixed;
        top: 64px;
    }

    .toc-widget {
        width: 345px;
        padding-left: 20px;
        background-color: rgb(255, 255, 255,0.7);
        border-radius: 10px;
        box-shadow: 0 10px 35px 2px rgba(0, 0, 0, .15), 0 5px 15px rgba(0, 0, 0, .07), 0 2px 5px -5px rgba(0, 0, 0, .1) !important;
    }

    .toc-widget .toc-title {
        padding: 35px 0 15px 17px;
        font-size: 1.5rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    .toc-widget ol {
        padding: 0;
        list-style: none;
    }

    #toc-content {
        padding-bottom: 30px;
        overflow: auto;
        max-height: 480px;
    }

    #toc-content ol {
        padding-left: 10px;
    }

    #toc-content ol li {
        padding-left: 10px;
    }

    #toc-content .toc-link:hover {
        color: #42b983;
        font-weight: 700;
        text-decoration: underline;
    }

    #toc-content .toc-link::before {
        background-color: transparent;
        max-height: 25px;

        position: absolute;
        right: 23.5vw;
        display: block;
    }

    #toc-content .is-active-link {
        color: #42b983;
    }

    #floating-toc-btn {
        position: fixed;
        right: 15px;
        bottom: 76px;
        padding-top: 15px;
        margin-bottom: 0;
        z-index: 998;
    }

    #floating-toc-btn .btn-floating {
        width: 48px;
        height: 48px;
    }

    #floating-toc-btn .btn-floating i {
        line-height: 48px;
        font-size: 1.4rem;
    }
</style>
<div class="row">
    <div id="main-content" class="col s12 m12 l9">
        <!-- 文章内容详情 -->
<div id="artDetail">
    <div class="card">
        <div class="card-content article-info">
            <div class="row tag-cate">
                <div class="col s7">
                    
                          <div class="article-tag">
                            <span class="chip bg-color">无标签</span>
                          </div>
                    
                </div>
                <div class="col s5 right-align">
                    
                    <div class="post-cate">
                        <i class="fas fa-bookmark fa-fw icon-category"></i>
                        
                            <a href="/blog/categories/Spring/" class="post-category">
                                Spring
                            </a>
                        
                    </div>
                    
                </div>
            </div>

            <div class="post-info">
                

                

                

                

                
            </div>
        </div>
        <hr class="clearfix">

        
        <!-- 是否加载使用自带的 prismjs. -->
        <link rel="stylesheet" href="/blog/libs/prism/prism.css">
        

        
        <!-- 代码块折行 -->
        <style type="text/css">
            code[class*="language-"], pre[class*="language-"] { white-space: pre-wrap !important; }
        </style>
        

        <div class="card-content article-card-content">
            <div id="articleContent">
                <h2 id="springboot-actuator"><a href="#springboot-actuator" class="headerlink" title="springboot-actuator"></a>springboot-actuator</h2><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/5YR7NJRjFpuZraAzu3qXlg">SpringBoot监控神器-Actuator保姆级教程</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Bs5Pp5oM-uQQPpfvUpzyQQ">手把手教你实现SpringBoot的监控！</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/m2n-MvFpjPx7H6BE6_rU9g">Spring Cloud应用的优雅下线与灰度发布</a></th>
</tr>
</thead>
</table>
<h2 id="springboot-admin"><a href="#springboot-admin" class="headerlink" title="springboot-admin"></a>springboot-admin</h2><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/r-W-gh_0hw2FzRaEzcUvNA">Spring Boot Admin横空出世！</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/J9yYprryj1ukKLHAemhdhw">Spring Boot Admin服务监控利器</a></th>
<th align="center"></th>
</tr>
</thead>
</table>
<h2 id="springboot-aop"><a href="#springboot-aop" class="headerlink" title="springboot-aop"></a>springboot-aop</h2><p><strong><a target="_blank" rel="noopener" href="https://www.baidu.com/s?tn=39042058_22_oem_dg&ie=utf-8&wd=aop%20execution%E8%A1%A8%E8%BE%BE%E5%BC%8F">aop execution表达式</a></strong></p>
<table>
<thead>
<tr>
<th align="center">类别</th>
<th align="center">函数</th>
<th align="center">入参</th>
<th align="center">说明</th>
</tr>
</thead>
<tbody><tr>
<td align="center">方法切点函数</td>
<td align="center">execution()</td>
<td align="center">方法匹配模式串</td>
<td align="center">表示满足某一匹配模式的所有目标类方法连接点。如execution(* greetTo(..))表示所有目标类中的greetTo()方法。 *</td>
</tr>
<tr>
<td align="center"></td>
<td align="center">@annotation()</td>
<td align="center">方法注解类名</td>
<td align="center">表示标注了特定注解的目标方法连接点。如@annotation(com.baobaotao.anno.NeedTest)表示任何标注了@NeedTest注解的目标类方法。</td>
</tr>
<tr>
<td align="center">方法入参切点函数</td>
<td align="center">args()</td>
<td align="center">类名</td>
<td align="center">通过判别目标类方法运行时入参对象的类型定义指定连接点。如args(com.baobaotao.Waiter)表示所有有且仅有一个按类型匹配于Waiter的入参的方法。</td>
</tr>
<tr>
<td align="center"></td>
<td align="center">@args()</td>
<td align="center">类型注解类名</td>
<td align="center">通过判别目标方法的运行时入参对象的类是否标注特定注解来指定连接点。如@args(com.baobaotao.Monitorable)表示任何这样的一个目标方法：它有一个入参且入参对象的类标注@Monitorable注解。</td>
</tr>
<tr>
<td align="center">目标类切点函数</td>
<td align="center">within()</td>
<td align="center">类名匹配串</td>
<td align="center">表示特定域下的所有连接点。如within(com.baobaotao.service.*)表示com.baobaotao.service包中的所有连接点，也即包中所有类的所有方法，而within(com.baobaotao.service.*Service)表示在com.baobaotao.service包中，所有以Service结尾的类的所有连接点。</td>
</tr>
<tr>
<td align="center"></td>
<td align="center">target()</td>
<td align="center">类名</td>
<td align="center">假如目标类按类型匹配于指定类，则目标类的所有连接点匹配这个切点。如通过target(com.baobaotao.Waiter)定义的切点，Waiter、以及Waiter实现类NaiveWaiter中所有连接点都匹配该切点。</td>
</tr>
<tr>
<td align="center"></td>
<td align="center">@within()</td>
<td align="center">类型注解类名</td>
<td align="center">假如目标类按类型匹配于某个类A，且类A标注了特定注解，则目标类的所有连接点匹配这个切点。  如@within(com.baobaotao.Monitorable)定义的切点，假如Waiter类标注了@Monitorable注解，则Waiter以及Waiter实现类NaiveWaiter类的所有连接点都匹配。</td>
</tr>
<tr>
<td align="center"></td>
<td align="center">@target()</td>
<td align="center">类型注解类名</td>
<td align="center">目标类标注了特定注解，则目标类所有连接点匹配该切点。如@target(com.baobaotao.Monitorable)，假如NaiveWaiter标注了@Monitorable，则NaiveWaiter所有连接点匹配切点。</td>
</tr>
<tr>
<td align="center">代理类切点函数</td>
<td align="center">this()</td>
<td align="center">类名</td>
<td align="center">代理类按类型匹配于指定类，则被代理的目标类所有连接点匹配切点。这个函数比较难理解，这里暂不举例，留待后面详解。</td>
</tr>
</tbody></table>
<p><strong>相关文章</strong></p>
<table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/mv_HTv9tjvcjij36Mu7jUQ">深入聊一聊Spring AOP实现机制！</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/BRgsKmvrZWeqrb-E0hlr4A">Spring AOP，应该不会有比这更详细的介绍了！</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/aMnqanuvhxSBPanwin1e0w">Spring AOP、AspectJ、CGLIB都是什么关系？</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/laeJBOmix77TkWYZGWtQCQ">似懂非懂的AspectJ</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/5nq5HiY0E2UF0cCt1TPULQ">Spring AOP与AspectJ有什么区别和联系？</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/K4b9Lm-VV8zrgAAIPbRC6w">如果抛开Spring，如何自己实现AOP？</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/c1kYhwSXZspDClCUaeoBYQ">图解Spring AOP实现原理</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/G9hhDR-RTodwev8mhACyZg">Spring Boot中的AOP，到底是JDK还是Cglib动态代理？</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/oqtI6II5v7bs-FS7DylWOw">@After,@Around,@Before的执行顺序※</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/opcyaV5iLjYHaJuD9yEMUA">Spring中一个少见的引介增强IntroductionAdvisor</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/O-EGbaMCwHhToNvLvoCPfg">Spring AOP中，切点有多少种定义方式?</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/fsX45Sz47Xnnx5C_xQVRMg">Spring AOP中的代理对象是怎么创建出来的？</a></td>
</tr>
</tbody></table>
<h2 id="springboot-dynamicDB"><a href="#springboot-dynamicDB" class="headerlink" title="springboot-dynamicDB"></a>springboot-dynamicDB</h2><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_37502106/article/details/91044952">spring boot使用AbstractRoutingDataSource实现动态数据源切换</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Cm8dp7NhoEWwY0C-Fhv98Q">Spring Boot如何动态切换多数据源？</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611">dynamic-datasource-spring-boot-starter</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/h1qguwbQp6nNU-e9U1jlow">如何自定义SpringBoot多数据源的starter组件</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/g71F3cnLtD4zqJGFCeJckQ">手把手教你玩多数据源动态切换-dynamicDB1</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/v8-AY1Otbn34m0YjSV4imw">Spring Boot实现MySQL读写分离技术-dynamicDB2</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/K_MFSt9HrbTnKXbDOEtCdg">Spring Boot多数据源及事务解决方案</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/shTTO91pwZfoiP7fP2RDIQ">SpringBoot实现动态切换数据源，这样做才更优雅！</a></td>
<td align="center"></td>
</tr>
</tbody></table>
<h2 id="springboot-feature"><a href="#springboot-feature" class="headerlink" title="springboot-feature"></a>springboot-feature</h2><p><strong>参数读取</strong></p>
<table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s?__biz=Mzg2MDYzODI5Nw==&mid=2247493953&idx=1&sn=f042ddbfafcea9d4e0bbb2b2522974b4&source=41#wechat_redirect">SpringBoot多种读取配置文件中参数的方式</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/qm4VrNF61mIpVHr0laLwDw">SpringBoot的@Value注解，高级特性，真心强大！</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/EqgFtVtKPTmTUGTZJ-RBtg">Spring Boot中yaml文件：定义list集合、数组及注意事项</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/wzqVJ5qb_eN6Nkz4ouAE-w">6种方式读取SpringBoot的配置，老鸟都这么玩（原理+实战）</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/PqxST5qMTl4ImPYb67uYWA">Spring Boot优雅加载配置文件的几种姿势！</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/pNAU_w6RQIjzxfaadjV_pA">如何不重新编译让Spring Boot配置文件生效</a></td>
</tr>
</tbody></table>
<p><strong>druid</strong></p>
<table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/AJyM0DyZNczgGcXepotnjA">Spring Boot如何统计、监控SQL运行情况？</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/rmGAlano4LZToATCoevdxw">使用druid进行数据库加密</a></th>
<th align="center"></th>
</tr>
</thead>
</table>
<p><strong>优化</strong></p>
<table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/4DlMT007f8zM6PWPf8y4bQ">Spring Boot深度调优，6得飞起~</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/1jKr7TTrCMIMafgRMzTbVA">Spring Boot这样优化，让你的项目飞起来！</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/_Q_nLK1u1LSc3wDd0904Jw">7种方式，教你提升Spring Boot项目的吞吐量</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/tDbur3cH9Nck74lVgQu9rQ">SpringBoot内置Tomcat线程数优化配置，你学会了吗</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/4WTGFJHeNWMQxlsIeBkeKw">这样优化，0.059s启动一个SpringBoot项目</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/kbYHSUxs6GfgiumN0nBdRA">7min到40s:Spring Boot启动优化实践</a></td>
</tr>
</tbody></table>
<p><strong>other</strong></p>
<table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/vWZu_pjJIrqHzCyBcM70rg">优雅的实现Spring Boot异步线程间数据传递</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Bvn6JUaP53dPJRlrplEKSQ">Spring Boot请求路径可以定义成&#x2F;**&#x2F;**这种格式吗？</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/9zSrUGMmXbU6rW1LaoUFvQ">你绝对不知道的SpringBoot的外部化配置特性！</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/x3NMF8AdyJ0Bf15B4yS5Kw">Spring Boot如何实现插件化开发模式</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/bpmT30Nymm5Iz5Gt-EbX9w">Spring Boot业务组件化开发，维护起来很香</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/j1LSUJiYFvj9VxLn3hFcww">try-catch这样写才足够优雅</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/mNMYLKLLtvYlwpKD9XdKcQ">请不要自己写，Spring Boot非常实用的内置功能</a></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<p><strong>模式</strong></p>
<table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/IeEOA8p-9P6rQHJ2Z2wlqQ">巧妙利用SpringBoot应用责任连模式,让编程事半功倍</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/rjgk96CdAdOTtZwUmu0s4w">SpringBoot使用装饰器模式</a></th>
<th align="center"></th>
</tr>
</thead>
</table>
<p><strong>业务</strong></p>
<table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/tAevzEUPINE02CYdu_P15Q">Spring Boot实现抽奖大转盘</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/1LTHXVqzvPkoRAwqLnvtYQ">Spring Boot接入支付宝完整流程实战</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/XbTuLy6Auf1aMiyva2P8Kw">用了6年的Spring Boot项目部署方案！打包+Shell脚本部署详解，稳的一批!</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/zBUt0WXV2807d_otijG2yw">Spring Boot+Prometheus+Grafana打造可视化监控一条龙！</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Oq-sXnjsOGpVJ93A8shKsw">监控神器：Prometheus轻松入门，真香</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/40StkNFGUVkZZB2_75NoaA">如何把Spring Boot的Jar包做成exe？</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/G3EE2n4QPqxMdQzhRHJxoQ">如何把java代码，打包成jar文件并转换为exe可执行文件？</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/nDMoT6uaubzcjAv_Hpx8VQ">手把手教你用Jenkins自动部署SpringBoot！</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/EczAna4q1DyeiHDPMkdYJw">Spring Boot使用Disruptor做内部高性能消息队列</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/9rs-2khxngoojE_0Y6G3cA">SpringBoot+Vue实现微信扫码支付、退款功能</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/hBRcsLN_u6ciW1tgBp7a5A">Spring Boot应用该如何预防XSS攻击 ？</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ze6JYUCQ75jOIUz9OoCfXA">Spring Boot整合Canal+RabbitMQ监听数据变更</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Kwp3CTAhF2ewW-cFoROPrQ">SpringBoot整合RocketMQ，尝尝几大高级特性！</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/97sSiT3kVvJFQ0aCW_Blxg">Spring Boot实现MySQL百万级数据量导出并避免OOM的解决方案</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/R4ZXK_FRNuFpl0d6hOD_Zg">SpringBoot整合MinIO实现视频的分片上传&#x2F;断点续传</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/HWO0263J3p097Odr63s9zA">Spring Boot项目Jar包加密，防止反编译</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/6V-_nEqAUhZxREQp6ZNdQw">Spring Boot接口防抖(防重复提交)，轻松搞定</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/bKniveockhmRgts89RNu4Q">SpringBoot+mail轻松实现各类邮件自动推送</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Wf0-9jXnkKfyEYdKuMvL-w">SpringBoot + AOP编程，轻松搞定审计日志</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/qoMEaRXIdNGIRKpBbigBTQ">SpringBoot实战：文件上传之秒传、断点续传、分片上传</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/dlKA4EBlee7YkXxQ0neHNQ">Spring Boot+Bucket4j实现API请求限流</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/2Pjjj-_bkLLAO-IkWfoPcg">Spring Boot + Canal实现数据库变化的实时监控</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/jn5BTbddcwDm1UptzG-zAA">Spring Boot关于日期时间格式化处理方式总结</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/kCWIT_61PFOYw-GMGXVTZQ">一个依赖搞定Spring Boot反爬虫，防止接口盗刷！</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/xjPdcTST-UxnNb8yNw1isQ">Spring Boot实现服务热部署的正确姿势</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/vknu8fy7JXxectA2zcW_mQ">SpringBoot 优雅停机的正确姿势</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/nEeStkLgSvFdwCt56WNvcg">Spring Boot别再用Date作为入参了，LocalDateTime、LocalDate真香！</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/v26j08vCqpH-O9jZaNKPDA">SpringBoot玩一玩代码混淆，防止反编译代码泄露</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/3pjRUugA56Yqwfm4geeadg">Spring Boot + Spring Batch实现批处理任务</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/zBeHyIzR1b4I2x3LU40ceQ">SpringBoot+Filter实现Gzip压缩超大json对象，传输耗时大大减少</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Ai23Q6BsBqE12yT3wg6M7w">Spring Boot配置日志输出的正确姿势！</a></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<p><strong>retry</strong></p>
<table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/bu4JIWUH5OlokH4IS3pK6g">Spring中的重试机制，简单、实用!(Spring Retry)</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/YXrMgbzgRlFGMf-BhwBupQ">重试框架Spring-Retry和Guava-Retry，你知道该怎么选吗？</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/U_nm92ujCGArkii5ze7uaA">Java远程调用失败？如何优雅的进行重试？</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/wfYmHJutFAnFnSJ5yV43lg">Spring Boot中使用spring-retry轻松解决重试</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/IFfvVrfXYcp2oerA5WGALg">接口请求重试的8种方法，你用哪种？</a></td>
<td align="center"></td>
</tr>
</tbody></table>
<p><strong>Thymeleaf模板引擎</strong></p>
<table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/aotHeEvGl3usy5BkBBwrFA">Thymeleaf快速入门</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Uvv1q3iQn2IwAB1crHWS1g">细品Spring Boot+Thymeleaf，还有这么多好玩的细节！</a></th>
<th align="center"></th>
</tr>
</thead>
</table>
<p><strong>Spring-Boot-Devtools</strong></p>
<blockquote>
<p>Spring Boot应用支持热部署，无需手动重启Spring Boot应用,spring-boot-devtools是一个为开发者服务的一个模块，其中最重要的功能就是修改代码后自动启动springboot服务，速度比手动停止后再启动要快，节省出来的并不是手工操作的时间，具体原理主要是使用了两个ClassLoader，一个Classloader加载不会改变的类（第三方Jar包），另一个ClassLoader加载会更改的类，称为restart ClassLoader,这样在有代码更改的时候，原来的restartClassLoader被丢弃，重新创建一个restartClassLoader，由于需要加载的类相比较少，所以实现了较快的重启时间<br><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/6eW96WABLYo7SGJV3BxXmg">Spring Boot开发环境热部署(HotSwap)详解</a></p>
</blockquote>
<h2 id="springboot-flowable"><a href="#springboot-flowable" class="headerlink" title="springboot-flowable"></a>springboot-flowable</h2><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/4Z4x1Iox1WEA6ojfPRYLQQ">Spring Boot + Flowable快速实现工作流，So Easy！</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Pd0MEkZu12vdmGGBT9ZV-w">Spring Boot整合流程引擎Flowable，so easy！</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/QEXceZ0KmDKFMcYq-B4Wkg">SpringBoot+Vue+Flowable，模拟一个请假审批流程！</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/npgP8WyVHDB3PFS-zPcI5w">49张图带领小伙伴们体验一把Flowable-UI</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/OTqIsuvCltIYkSYZCQgk3Q">Spring Security + Vue + Flowable怎么玩？</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/H1TNq5eUEN4H-B0oR12mpg">极简Java工作流概念入门</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://gitee.com/tony2y/RuoYi-flowable">RuoYi-flowable：基RuoYi-vue + flowable 6.7.2的工作流管理</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/5Mcs73Xn8ZZi3S0euPnoSg">Flowable79张表都是干嘛的？</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/lZ4l_1WGNP5S4g8QBTUdAg">请假要组长和经理同时审批该怎么办？来看看工作流中的会签功能！</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/tc5OdOUONt5qPbT9ZbmQYQ">一个不用写代码的案例，来看看Flowable到底给我们提供了哪些功能？</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/VOGB9JRQKxjs1bVxYJuYpA">手把手教大家编译flowable源码</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/9S7RLcE_Xox6i3yju3q_dw">手把手教大家在Spring Boot中处理flowable中的用户和组！</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/EgbDiYjURD9yU0oFMeZowA">Flowable流程部署与删除</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/3UTii7TzyPGJIOc-68YThw">玩转Flowable流程实例</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/uXgorkmhHcDq_VTHJNLhZw">如何查询已经执行过的流程信息？</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/WSKc6LVtnCo7dgSbqcUqeg">Flowable流程实例的挂起（暂停）与激活</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/CbqaQMMDlJ9HLuH-bOP3ew">如何使用流程中的DataObject并为流程设置租户</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/f55gKMhvp1AQ47Bw1gaPMA">Flowable中ReceiveTask怎么玩？</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/GnYHXlLrzxCBYvo0uv_j_w">Flowable设置任务处理人的四种方式</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/wCPkTnm93kiobSOaK_LpOw">Flowable任务如何认领，回退？</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ftVZghFQKaukZI6H0DhbOg">Flowable按角色分配任务</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/k4zi7PhJfQFVKDWGBc7K2g">Flowable服务任务执行的三种方式</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/BBuJU5EZRJYwWMmcPT96_g">在Java代码中来一段JavaScript？聊聊Flowable中的脚本任务</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/VqQ0Zp-YXvydZb6Gy5Iyag">体验一把Flowable三种常见网关</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/qvm8tGucb7V_JOzAyiXcmQ">Flowable设置流程变量的四种方式</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/1qgDJ63Q09vNOzMGpzA-iA">Flowable已经执行完毕的流程去哪找？</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/jHLj_vwF5yiYWz3E8wRUWw">Flowable定时器的各种玩法</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/OjCTtrVx8kIvGWkobdv9Gg">Spring Boot + Flyway，自动化实现数据库版本控制</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/gk_hp8YUZB5L3gpdy7aCPws">流程表单初体验</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/DKPc9SWk6nWqLS9RQLV79Q">Flowable外置的HTML表单怎么玩？</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/KnUH8uerQa0fWPdAkl8zCQ">使用JSON格式来定义Flowable外置表单</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ETgKwsaceoUDp3cqKVtVtQ">一图胜千言，实时掌握流程走到哪一步了</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Svlv9DbwZdK_gkSAatF-zQ">Flowable系列</a></td>
</tr>
</tbody></table>
<h2 id="springboot-freemarker"><a href="#springboot-freemarker" class="headerlink" title="springboot-freemarker"></a>springboot-freemarker</h2><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://www.cnblogs.com/linjiqin/p/3388298.html">freemarker语法</a></th>
<th align="center"><a target="_blank" rel="noopener" href="http://deepoove.com/poi-tl/">poi-tl文档</a></th>
<th align="center"></th>
</tr>
</thead>
</table>
<h2 id="springboot-i18n"><a href="#springboot-i18n" class="headerlink" title="springboot-i18n"></a>springboot-i18n</h2><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/yDCk6I0Qnycw2JYP4UWd8g">一探究竟：深度解析Java国际化底层类ResourceBundle</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/aNZg2zW8mz5Kr7Nh4Sri3w">Spring Boot国际化踩坑指南</a></th>
<th align="center"></th>
</tr>
</thead>
</table>
<h2 id="springboot-jasypt"><a href="#springboot-jasypt" class="headerlink" title="springboot-jasypt"></a>springboot-jasypt</h2><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/bNGjVDdGRT2Jj0W5tZzsUQ">两种方式，实现SpringBoot中数据库密码加密</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://blog.csdn.net/youanyyou/article/details/123492090">Spring Boot保护敏感配置的4种方法</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/t5wlql3kBPlsnhdJPvxeOQ">Spring Boot加密配置中的敏感信息</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/IvuC0mN0uIs62Xa8LeOW1Q">Springboot配置文件、隐私数据脱敏的最佳实践</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/CN2niF4yTpOs3cZYBcWDeQ">Spring Boot配置文件这样加密，才足够安全！</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/xr76Xxe6BudJXea5aCjGAw">仅需三步完成SpringBoot日志脱敏</a></td>
</tr>
</tbody></table>
<h2 id="springboot-kafka"><a href="#springboot-kafka" class="headerlink" title="springboot-kafka"></a>springboot-kafka</h2><p><strong>consumer</strong></p>
<pre class="line-numbers language-properties" data-language="properties"><code class="language-properties"><span class="token comment"># 消费者所属消费组的唯一标识</span>
group.id
<span class="token comment"># 一次拉取请求的最大消息数，默认500条</span>
max.poll.records
<span class="token comment"># 指定拉取消息线程最长空闲时间，默认300000ms</span>
max.poll.interval.ms
<span class="token comment"># 检测消费者是否失效的超时时间，默认10000ms</span>
session.timeout.ms
<span class="token comment"># 消费者心跳时间，默认3000ms</span>
heartbeat.interval.ms
<span class="token comment"># 连接集群broker地址</span>
bootstrap.servers
<span class="token comment"># 是否开启自动提交消费位移的功能，默认true</span>
enable.auto.commit
<span class="token comment"># 自动提交消费位移的时间间隔，默认5000ms</span>
auto.commit.interval.ms
<span class="token comment"># 消费者的分区配置策略,默认RangeAssignor</span>
partition.assignment.strategy
<span class="token comment"># 如果分区没有初始偏移量，或者当前偏移量服务器上不存在时，将使用的偏移量设置，earliest从头开始消费，latest从最近的开始消费，none抛出异常，如果存在已经提交的offest时,不管设置为earliest或者latest都会从已经提交的offest处开始消费,如果不存在已经提交的offest时,earliest表示从头开始消费,latest表示从最新的数据消费,也就是新产生的数据.none topic各分区都存在已提交的offset时，从提交的offest处开始消费；只要有一个分区不存在已提交的offset，则抛出异常.kafka-0.10.1.X版本之前: auto.offset.reset的值为smallest,和,largest.(offest保存在zk中).kafka-0.10.1.X版本之后: auto.offset.reset的值更改为:earliest,latest,和none(offest保存在kafka的一个特殊的topic,名为:__consumer_offsets里面)</span>
auto.offset.reset
<span class="token comment"># 消费者客户端一次请求从Kafka拉取消息的最小数据量，如果Kafka返回的数据量小于该值，会一直等待，直到满足这个配置大小，默认1b</span>
fetch.min.bytes
<span class="token comment"># 消费者客户端一次请求从Kafka拉取消息的最大数据量，默认50MB</span>
fetch.max.bytes
<span class="token comment"># 从Kafka拉取消息时，在不满足fetch.min.bytes条件时，等待的最大时间，默认500ms</span>
fetch.max.wait.ms
<span class="token comment"># 强制刷新元数据时间，毫秒，默认300000，5分钟</span>
metadata.max.age.ms
<span class="token comment"># 设置从每个分区里返回给消费者的最大数据量，区别于fetch.max.bytes，默认1MB</span>
max.partition.fetch.bytes
<span class="token comment"># Socket发送缓冲区大小，默认128kb,-1将使用操作系统的设置</span>
send.buffer.bytes
<span class="token comment"># Socket发送缓冲区大小，默认64kb,-1将使用操作系统的设置</span>
receive.buffer.bytes
<span class="token comment"># 消费者客户端的id</span>
client.id
<span class="token comment"># 连接失败后，尝试连接Kafka的时间间隔，默认50ms</span>
reconnect.backoff.ms
<span class="token comment"># 尝试连接到Kafka，生产者客户端等待的最大时间，默认1000ms</span>
reconnect.backoff.max.ms
<span class="token comment"># 消息发送失败重试时间间隔，默认100ms</span>
retry.backoff.ms
<span class="token comment"># 样本计算时间窗口，默认30000ms</span>
metrics.sample.window.ms
<span class="token comment"># 用于维护metrics的样本数量，默认2</span>
metrics.num.samples
<span class="token comment"># metrics日志记录级别，默认info</span>
metrics.log.level
<span class="token comment"># 类的列表，用于衡量指标，默认空list</span>
metric.reporters
<span class="token comment"># 自动检查CRC32记录的消耗</span>
check.crcs
<span class="token comment"># key反序列化方式</span>
key.deserializer
<span class="token comment"># value反序列化方式</span>
value.deserializer
<span class="token comment"># 设置多久之后关闭空闲连接，默认540000ms</span>
connections.max.idle.ms
<span class="token comment"># 客户端将等待请求的响应的最大时间,如果在这个时间内没有收到响应，客户端将重发请求，超过重试次数将抛异常，默认30000ms</span>
request.timeout.ms
<span class="token comment"># 设置消费者api超时时间，默认60000ms</span>
default.api.timeout.ms
<span class="token comment"># 自定义拦截器</span>
interceptor.classes
<span class="token comment"># 内部的主题:consumer_offsets和一transaction_state。该参数用来指定Kafka中的内部主题是否可以向消费者公开，默认值为true。如果设置为true，那么只能使用subscribe(Collection)的方式而不能使用subscribe(Pattern)的方式来订阅内部主题，设置为false则没有这个限制。</span>
exclude.internal.topics
<span class="token comment"># 用来配置消费者的事务隔离级别。如果设置为“read committed”，那么消费者就会忽略事务未提交的消息，即只能消费到LSO(LastStableOffset)的位置，默认情况下为“read_uncommitted”，即可以消费到HW(High Watermark)处的位置</span>
isolation.level
<span class="token key attr-name">key.deserializer</span> <span class="token punctuation">=</span> <span class="token value attr-value">org.apache.kafka.common.serialization.StringDeserializer</span>
<span class="token key attr-name">value.deserializer</span> <span class="token punctuation">=</span> <span class="token value attr-value">org.apache.kafka.common.serialization.StringDeserializer</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p><strong>producer</strong></p>
<pre class="line-numbers language-properties" data-language="properties"><code class="language-properties"><span class="token comment"># Snappy压缩技术是Google开发的，它可以在提供较好的压缩比的同时，减少对CPU的使用率并保证好的性能，所以建议在同时考虑性能和带宽的情况下使用。Gzip压缩技术通常会使用更多的CPU和时间，但会产生更好的压缩比，所以建议在网络带宽更受限制的情况下使用，默认不压缩，该参数可以设置成snappy、gzip或lz4对发送给broker的消息进行压缩</span>
<span class="token key attr-name">compression.type</span><span class="token punctuation">=</span><span class="token value attr-value">Gzip</span>
<span class="token comment"># 请求的最大字节数。这也是对最大消息大小的有效限制。注意：server具有自己对消息大小的限制，这些大小和这个设置不同。此项设置将会限制producer每次批量发送请求的数目，以防发出巨量的请求。</span>
<span class="token key attr-name">max.request.size</span><span class="token punctuation">=</span><span class="token value attr-value">1048576</span>
<span class="token comment"># TCP的接收缓存SO_RCVBUF空间大小，用于读取数据</span>
<span class="token key attr-name">receive.buffer.bytes</span><span class="token punctuation">=</span><span class="token value attr-value">32768</span>
<span class="token comment"># client等待请求响应的最大时间,如果在这个时间内没有收到响应，客户端将重发请求，超过重试次数发送失败</span>
<span class="token key attr-name">request.timeout.ms</span><span class="token punctuation">=</span><span class="token value attr-value">30000</span>
<span class="token comment"># TCP的发送缓存SO_SNDBUF空间大小，用于发送数据</span>
<span class="token key attr-name">send.buffer.bytes</span><span class="token punctuation">=</span><span class="token value attr-value">131072</span>
<span class="token comment"># 指定server等待来自followers的确认的最大时间，根据acks的设置，超时则返回error</span>
<span class="token key attr-name">timeout.ms</span><span class="token punctuation">=</span><span class="token value attr-value">30000</span>
<span class="token comment"># 在block前一个connection上允许最大未确认的requests数量。当设为1时，即是消息保证有序模式，注意：这里的消息保证有序是指对于单个Partition的消息有顺序，因此若要保证全局消息有序，可以只使用一个Partition，当然也会降低性能</span>
<span class="token key attr-name">max.in.flight.requests.per.connection</span><span class="token punctuation">=</span><span class="token value attr-value">5</span>
<span class="token comment"># 在第一次将数据发送到某topic时，需先fetch该topic的metadata，得知哪些服务器持有该topic的partition，该值为最长获取metadata时间</span>
<span class="token key attr-name">metadata.fetch.timeout.ms</span><span class="token punctuation">=</span><span class="token value attr-value">60000</span>
<span class="token comment"># 连接失败时，当我们重新连接时的等待时间</span>
<span class="token key attr-name">reconnect.backoff.ms</span><span class="token punctuation">=</span><span class="token value attr-value">50</span>
<span class="token comment"># 在重试发送失败的request前的等待时间，防止若目的Broker完全挂掉的情况下Producer一直陷入死循环发送，折中的方法</span>
<span class="token key attr-name">retry.backoff.ms</span><span class="token punctuation">=</span><span class="token value attr-value">100</span>
<span class="token comment"># metrics系统维护可配置的样本数量，在一个可修正的window size</span>
<span class="token key attr-name">metrics.sample.window.ms</span><span class="token punctuation">=</span><span class="token value attr-value">30000</span>
<span class="token comment"># 用于维护metrics的样本数</span>
<span class="token key attr-name">metrics.num.samples</span><span class="token punctuation">=</span><span class="token value attr-value">2</span>
<span class="token comment"># 类的列表，用于衡量指标。实现MetricReporter接口</span>
<span class="token key attr-name">metric.reporters</span><span class="token punctuation">=</span><span class="token value attr-value">[]</span>
<span class="token comment"># 强制刷新metadata的周期，即使leader没有变化</span>
<span class="token key attr-name">metadata.max.age.ms</span><span class="token punctuation">=</span><span class="token value attr-value">300000</span>
<span class="token comment"># 与broker会话协议，取值：LAINTEXT,SSL,SASL_PLAINTEXT,SASL_SSL</span>
<span class="token key attr-name">security.protocol</span><span class="token punctuation">=</span><span class="token value attr-value">PLAINTEXT</span>
<span class="token comment"># 分区类，实现Partitioner接口</span>
<span class="token key attr-name">partitioner.class</span><span class="token punctuation">=</span><span class="token value attr-value">class org.apache.kafka.clients.producer.internals.DefaultPartitioner</span>
<span class="token comment"># 控制block的时长，当buffer空间不够或者metadata丢失时产生block</span>
<span class="token key attr-name">max.block.ms</span><span class="token punctuation">=</span><span class="token value attr-value">60000</span>
<span class="token comment"># 关闭达到该时间的空闲连接</span>
<span class="token key attr-name">connections.max.idle.ms</span><span class="token punctuation">=</span><span class="token value attr-value">540000</span>
<span class="token comment"># 当向server发出请求时，这个字符串会发送给server，目的是能够追踪请求源</span>
<span class="token key attr-name">client.id</span><span class="token punctuation">=</span><span class="token value attr-value">""</span>
<span class="token comment"># acks=0配置适用于实现非常高的吞吐量,acks=all这是最安全的模式。Server完成producer request前需要确认的数量。acks=0时，producer不会等待确认，直接添加到socket等待发送；acks=1时，等待leader写到local log就行；acks=all或acks=-1时，等待isr中所有副本确认（注意：确认都是broker接收到消息放入内存就直接返回确认，不是需要等待数据写入磁盘后才返回确认，这也是kafka快的原因）</span>
<span class="token key attr-name">acks</span> <span class="token punctuation">=</span> <span class="token value attr-value">all</span>
<span class="token comment"># 发生错误时，重传次数。当开启重传时，需要将`max.in.flight.requests.per.connection`设置为1，否则可能导致失序</span>
<span class="token key attr-name">retries</span> <span class="token punctuation">=</span> <span class="token value attr-value">0</span>
<span class="token comment"># 发送到同一个partition的消息会被先存储在batch中，该参数指定一个batch可以使用的内存大小，单位是byte。不一定需要等到batch被填满才能发送Producer可以将发往同一个Partition的数据做成一个Produce Request发送请求，即Batch批处理，以减少请求次数，该值即为每次批处理的大小。另外每个Request请求包含多个Batch，每个Batch对应一个Partition，且一个Request发送的目的Broker均为这些partition的leader副本。若将该值设为0，则不会进行批处理</span>
<span class="token key attr-name">batch.size</span> <span class="token punctuation">=</span> <span class="token value attr-value">16384</span>
<span class="token comment"># 生产者在发送消息前等待linger.ms，从而等待更多的消息加入到batch中。如果batch被填满或者linger.ms达到上限，就把batch中的消息发送出去,Producer默认会把两次发送时间间隔内收集到的所有Requests进行一次聚合然后再发送，以此提高吞吐量，而linger.ms则更进一步，这个参数为每次发送增加一些delay，以此来聚合更多的Message。官网解释翻译：producer会将request传输之间到达的所有records聚合到一个批请求。通常这个值发生在欠负载情况下，record到达速度快于发送。但是在某些场景下，client即使在正常负载下也期望减少请求数量。这个设置就是如此，通过人工添加少量时延，而不是立马发送一个record,producer会等待所给的时延，以让其他records发送出去，这样就会被聚合在一起。这个类似于TCP的Nagle算法。该设置给了batch的时延上限：当我们获得一个partition的batch.size大小的records，就会立即发送出去，而不管该设置；但是如果对于这个partition没有累积到足够的record，会linger指定的时间等待更多的records出现。该设置的默认值为0(无时延)。例如，设置linger.ms=5，会减少request发送的数量，但是在无负载下会增加5ms的发送时延。</span>
<span class="token key attr-name">linger.ms</span> <span class="token punctuation">=</span> <span class="token value attr-value">1</span>
<span class="token comment"># Producer可以用来缓存数据的内存大小。该值实际为RecordAccumulator类中的BufferPool，即Producer所管理的最大内存。如果数据产生速度大于向broker发送的速度，producer会阻塞max.block.ms，超时则抛出异常</span>
<span class="token key attr-name">buffer.memory</span> <span class="token punctuation">=</span> <span class="token value attr-value">33554432</span>
<span class="token key attr-name">key.serializer</span> <span class="token punctuation">=</span> <span class="token value attr-value">org.apache.kafka.common.serialization.StringSerializer</span>
<span class="token key attr-name">value.serializer</span> <span class="token punctuation">=</span> <span class="token value attr-value">org.apache.kafka.common.serialization.StringSerializer</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/MuXC-nYceLOsBo76z0KvyA">Spring Kafka之@KafkaListener单条或批量处理消息</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/3mwx_DCK8ExwANepk8ysng">SpringBoot整合Kafka实现千万级数据异步处理，实战介绍！</a></th>
<th align="center"></th>
</tr>
</thead>
</table>
<h2 id="springboot-lifecycle"><a href="#springboot-lifecycle" class="headerlink" title="springboot-lifecycle"></a>springboot-lifecycle</h2><p><strong>Aware接口</strong></p>
<img alt="" src="https://mmbiz.qpic.cn/mmbiz_png/GvtDGKK4uYnMAdNBhDEt2oKT6McDSjsYtiarVn6kWpCtRp05sbErlm1icevfuowMiakiayqDtMf7jTjkryYlIxicTlA/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1" style="zoom:50%;" />

<p>每一个Aware的作用如下：</p>
<ul>
<li><strong>ApplicationEventPublisherAware</strong>：实现该接口的对象可以获取事件发布的能力。</li>
<li><strong>ServletContextAware</strong>：实现该接口的对象可以获取到ServletContext对象。</li>
<li><strong>MessageSourceAware</strong>：实现该接口的对象可以获取到MessageSource对象，MessageSource支持多消息源，主要用于主要用于国际化。</li>
<li><strong>ResourceLoaderAware</strong>：实现该接口的对象可以获取到一个ResourceLoader，Spring ResourceLoader则为我们提供了一个统一的getResource()方法来通过资源路径检索外部资源，例如文本文件、XML文件、属性文件或图像文件等。</li>
<li><strong>ApplicationStartupAware</strong>：实现该接口的对象可以获取到一个ApplicationStartup对象，这个比较新，是Spring5.3中新推出的，通过ApplicationStartup可以标记应用程序启动期间的步骤，并收集有关执行上下文或其处理时间的数据。</li>
<li><strong>NotificationPublisherAware</strong>：实现该接的对象可以获取到一个NotificationPublisher对象，通过该对象可以实现通知的发送。</li>
<li><strong>EnvironmentAware</strong>：实现该接口的对象可以获取到一个Environment对象，通过Environment可以获取到容器的环境信息。</li>
<li><strong>BeanFactoryAware</strong>：实现该接口的对象可以获取到一个BeanFactory对象，通过BeanFactory可以完成Bean的查询等操作。</li>
<li><strong>ImportAware</strong>：实现该接口的对象可以获取到一个AnnotationMetadata对象，ImportAware接口是需要和@Import注解一起使用的。在@Import作为元注解使用时，通过@Import导入的配置类如果实现了ImportAware接口就可以获取到导入该配置类接口的数据配置。</li>
<li><strong>EmbeddedValueResolverAware</strong>：实现该接口的对象可以获取到一个StringValueResolver对象，通过StringValueResolver对象，可以读取到Spring容器中的properties配置的值（YAML配置也可以）。</li>
<li><strong>ServletConfigAware</strong>：实现该接口的对象可以获取到一个ServletConfig对象，不过这个似乎没什么用，我们很少自己去配置ServletConfig。</li>
<li><strong>LoadTimeWeaverAware</strong>：实现该接口的对象可以获取到一个LoadTimeWeaver对象，通过该对象可以获取加载Spring Bean时织入的第三方模块，如AspectJ等。</li>
<li><strong>BeanClassLoaderAware</strong>：实现该接口的对象可以获取到一个ClassLoader对象。</li>
<li><strong>BeanNameAware</strong>：实现该接口的对象可以获取到一个当前Bean的名称。</li>
<li><strong>ApplicationContextAware</strong>：实现该接口的对象可以获取到一个ApplicationContext对象，通过-ApplicationContext可以获取容器中的Bean、环境等信息。</li>
</ul>
<p><strong>Spring Boot监听器</strong></p>
<p>Spring Boot启动事件顺序ApplicationListener中的泛型的类型,可以在MyEvent里面实现某一个接口来监听不同的事件</p>
<ol>
<li><strong>ApplicationStartingEvent</strong><br>这个事件在Spring Boot应用运行开始时，且进行任何处理之前发送（除了监听器和初始化器注册之外）。</li>
<li><strong>ApplicationEnvironmentPreparedEvent</strong><br>这个事件在当已知要在上下文中使用Spring环境（Environment）时，在Spring上下文（context）创建之前发送。</li>
<li><strong>ApplicationContextInitializedEvent</strong><br>这个事件在当Spring应用上下文（ApplicationContext）准备好了，并且应用初始化器（ApplicationContextInitializers）已经被调用，在bean的定义（bean definitions）被加载之前发送。</li>
<li><strong>ApplicationPreparedEvent</strong><br>这个事件是在Spring上下文（context）刷新之前，且在bean的定义（bean definitions）被加载之后发送。</li>
<li><strong>ApplicationStartedEvent</strong><br>这个事件是在Spring上下文（context）刷新之后，且在application&#x2F;command-line runners被调用之前发送。</li>
<li><strong>AvailabilityChangeEvent</strong><br>这个事件紧随上个事件之后发送，状态：ReadinessState.CORRECT，表示应用已处于活动状态。</li>
<li><strong>ApplicationReadyEvent</strong><br>这个事件在任何application&#x2F; command-line runners调用之后发送。</li>
<li><strong>AvailabilityChangeEvent</strong><br>这个事件紧随上个事件之后发送，状态：ReadinessState.ACCEPTING_TRAFFIC，表示应用可以开始准备接收请求了。</li>
<li><strong>ApplicationFailedEvent</strong><br>这个事件在应用启动异常时进行发送</li>
</ol>
<p>注册监听器方式1：<br><code>implements ApplicationListener&lt;T&gt;</code>接口在类上增加@Component注册bean<br>自定义事件代码如下：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Component</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CustomListener</span> <span class="token keyword">implements</span> <span class="token class-name">ApplicationListener</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">MyEvent</span><span class="token punctuation">></span></span><span class="token punctuation">&#123;</span>
    <span class="token annotation punctuation">@Override</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onApplicationEvent</span><span class="token punctuation">(</span><span class="token class-name">MyEvent</span> myEvent<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        myEvent<span class="token punctuation">.</span><span class="token function">printMsg</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span>
<span class="token annotation punctuation">@SuppressWarnings</span><span class="token punctuation">(</span><span class="token string">"serial"</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">MyEvent</span> <span class="token keyword">extends</span> <span class="token class-name">ApplicationEvent</span><span class="token punctuation">&#123;</span>
	<span class="token keyword">public</span> <span class="token class-name">MyEvent</span><span class="token punctuation">(</span><span class="token class-name">Object</span> source<span class="token punctuation">)</span><span class="token punctuation">&#123;</span><span class="token keyword">super</span><span class="token punctuation">(</span>source<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>注册监听器方式2：<br>使用监听器的时候如果不将监听器的类注册为spring bean则使用这种方式添加</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">ConfigurableApplicationContext</span> context <span class="token operator">=</span> <span class="token class-name">SpringApplication</span><span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span><span class="token class-name">XmxeApplication</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 装载监听</span>
context<span class="token punctuation">.</span><span class="token function">addApplicationListener</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name"><span class="token namespace">com<span class="token punctuation">.</span>xmxe<span class="token punctuation">.</span>config<span class="token punctuation">.</span>listen<span class="token punctuation">.</span></span>CustomListener</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>
<p>注册监听器方式3：<br>在<code>application.properties</code>中配置监听<code>context.listener.classes=com.xmxe.config.listen.CustomListener</code></p>
<p>注册监听器方式4：<br>创建MyListener4类，该类无需实现ApplicationListener接口，使用@EventListener装饰具体方法</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Component</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">MyListener4</span><span class="token punctuation">&#123;</span>
	<span class="token class-name">Logger</span> logger <span class="token operator">=</span> <span class="token class-name">Logger</span><span class="token punctuation">.</span><span class="token function">getLogger</span><span class="token punctuation">(</span><span class="token class-name">MyListener4</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token annotation punctuation">@EventListener</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">listener</span><span class="token punctuation">(</span><span class="token class-name">MyEvent</span> event<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
    logger<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string">"%s监听到事件源：%s."</span><span class="token punctuation">,</span> <span class="token class-name">MyListener4</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> event<span class="token punctuation">.</span><span class="token function">getSource</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">// 进行测试(在启动类中加入发布事件的逻辑)：</span>
<span class="token annotation punctuation">@SpringBootApplication</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">LisenterApplication</span><span class="token punctuation">&#123;</span>
	<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
		<span class="token class-name">ConfigurableApplicationContext</span> context <span class="token operator">=</span> <span class="token class-name">SpringApplication</span><span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span><span class="token class-name">LisenterApplication</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token comment">//装载事件</span>
		context<span class="token punctuation">.</span><span class="token function">addApplicationListener</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">MyListener1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token comment">//发布事件</span>
		context<span class="token punctuation">.</span><span class="token function">publishEvent</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">MyEvent</span><span class="token punctuation">(</span><span class="token string">"测试事件."</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/NVdxbd07kVLThlO6NujwNA">SpringBoot项目实现发布订阅模式，真的很简单！</a></li>
</ul>
<p><strong>ConfigurableApplicationContext方法</strong></p>
<blockquote>
<p>ConfigurableApplicationContext即为SpringApplication.run()返回值</p>
</blockquote>
<pre class="line-numbers language-java" data-language="java"><code class="language-java">
<span class="token keyword">static</span> <span class="token keyword">interface</span> <span class="token class-name">ConfigurableApplicationContext</span> <span class="token keyword">extends</span> <span class="token class-name">ApplicationContext</span><span class="token punctuation">,</span> <span class="token class-name">Lifecycle</span><span class="token punctuation">,</span> <span class="token class-name">Closeable</span> <span class="token punctuation">&#123;</span>

<span class="token comment">/**
 * 应用上下文配置时，这些符号用于分割多个配置路径
 */</span>
<span class="token class-name">String</span> <span class="token constant">CONFIG_LOCATION_DELIMITERS</span> <span class="token operator">=</span> <span class="token string">",; \t\n"</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * BeanFactory中，ConversionService类所对应的bean的名字。如果没有此类的实例的话则使用默认的转换规则
 */</span>
<span class="token class-name">String</span> <span class="token constant">CONVERSION_SERVICE_BEAN_NAME</span> <span class="token operator">=</span> <span class="token string">"conversionService"</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * LoadTimeWaver类所对应的Bean在容器中的名字。如果提供了该实例，上下文会使用临时的ClassLoader,这样，LoadTimeWaver就可以使用bean确切的类型了
 */</span>
<span class="token class-name">String</span> <span class="token constant">LOAD_TIME_WEAVER_BEAN_NAME</span> <span class="token operator">=</span> <span class="token string">"loadTimeWeaver"</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Environment类在容器中实例的名字
 */</span>
<span class="token class-name">String</span> <span class="token constant">ENVIRONMENT_BEAN_NAME</span> <span class="token operator">=</span> <span class="token string">"environment"</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * System系统变量在容器中对应的Bean的名字
 */</span>
<span class="token class-name">String</span> <span class="token constant">SYSTEM_PROPERTIES_BEAN_NAME</span> <span class="token operator">=</span> <span class="token string">"systemProperties"</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * System环境变量在容器中对应的Bean的名字
 */</span>
<span class="token class-name">String</span> <span class="token constant">SYSTEM_ENVIRONMENT_BEAN_NAME</span> <span class="token operator">=</span> <span class="token string">"systemEnvironment"</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * 设置容器的唯一ID
 */</span>
<span class="token keyword">void</span> <span class="token function">setId</span><span class="token punctuation">(</span><span class="token class-name">String</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * 设置此容器的父容器,需要注意的是，父容器一经设定就不应该修改。并且一般不会在构造方法中对其进行配置，因为很多时候其父容器还不可用。比如WebApplicationContext。
 */</span>
<span class="token keyword">void</span> <span class="token function">setParent</span><span class="token punctuation">(</span><span class="token class-name">ApplicationContext</span> parent<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * 设置容器的Environment变量
 */</span>
<span class="token keyword">void</span> <span class="token function">setEnvironment</span><span class="token punctuation">(</span><span class="token class-name">ConfigurableEnvironment</span> environment<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * 以COnfigurableEnvironment的形式返回此容器的环境变量。以使用户更好的进行配置
 */</span>
<span class="token annotation punctuation">@Override</span>
<span class="token class-name">ConfigurableEnvironment</span> <span class="token function">getEnvironment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * 此方法一般在读取应用上下文配置的时候调用，用以向此容器中增加BeanFactoryPostProcessor。增加的Processor会在容器refresh的时候使用。
 */</span>
<span class="token keyword">void</span> <span class="token function">addBeanFactoryPostProcessor</span><span class="token punctuation">(</span><span class="token class-name">BeanFactoryPostProcessor</span> postProcessor<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * 向容器增加一个ApplicationListener，增加的Listener用于发布上下文事件如refresh和shutdown等,需要注意的是，如果此上下文还没有启动，那么在此注册的Listener将会在上下文refresh的时候，全部被调用,如果上下文已经是active状态的了，就会在multicaster中使用
*/</span>
<span class="token keyword">void</span> <span class="token function">addApplicationListener</span><span class="token punctuation">(</span><span class="token class-name">ApplicationListener</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span> listener<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * 向容器中注入给定的Protocol resolver，允许多个实例同时存在。在此注册的每一个resolver都将会在上下的标准解析规则之前使用。因此，某种程度上来说这里注册的resolver可以覆盖上下文的resolver
 */</span>
<span class="token keyword">void</span> <span class="token function">addProtocolResolver</span><span class="token punctuation">(</span><span class="token class-name">ProtocolResolver</span> resolver<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * 加载资源配置文件（XML、properties,Whatever）。由于此方法是一个初始化方法，因此如果调用此方法失败的情况下，要将其已经创建的Bean销毁。换句话说，调用此方法以后，要么所有的Bean都实例化好了，要么就一个都没有实例化
 */</span>
<span class="token keyword">void</span> <span class="token function">refresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">BeansException</span><span class="token punctuation">,</span> <span class="token class-name">IllegalStateException</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * 向JVM注册一个回调函数，用以在JVM关闭时，销毁此应用上下文。
 */</span>
<span class="token keyword">void</span> <span class="token function">registerShutdownHook</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * 关闭此应用上下文，释放其所占有的所有资源和锁。并销毁其所有创建好的singleton Beans,实现的时候，此方法不应该调用其父上下文的close方法，因为其父上下文具有自己独立的生命周期.多次调用此方法，除了第一次，后面的调用应该被忽略。
 */</span>
<span class="token annotation punctuation">@Override</span>
<span class="token keyword">void</span> <span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * 检测此FactoryBean是否被启动过。
 */</span>
<span class="token keyword">boolean</span> <span class="token function">isActive</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * 返回此应用上下文的容器。千万不要使用此方法来对BeanFactory生成的Bean做后置处理，因为单例Bean在此之前已经生成,这种情况下应该使用BeanFactoryPostProcessor来在Bean生成之前对其进行处理。通常情况下，内容容器只有在上下文是激活的情况下才能使用。因此，在使用此方法前，可以调用isActive来判断容器是否可用
 */</span>
<span class="token class-name">ConfigurableListableBeanFactory</span> <span class="token function">getBeanFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IllegalStateException</span><span class="token punctuation">;</span>

<span class="token punctuation">&#125;</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p><strong>相关文章</strong></p>
<table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/EasYsc9ixVVCPRvPTCCkYg">SpringBoot 优雅停机的正确姿势</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ylmU2rT0JlnYJA9f1w065A">Spring Boot自定义监听事件</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/V0c6aZKih_luH6Qoqfgp4Q">Spring Boot如何使用拦截器、过滤器、监听器？</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/aUgTwv4iQNr3BFku_qhwvw">Spring Boot过滤器、拦截器、监听器对比及使用场景</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/mbstcEEtwOS9ZRSSjusbfA">手把手教你在SpringBoot中自定义参数解析器</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/YNFFBuokPHfQxcWTbdVfwQ">SpringBoot初始化几大招式，看了终于明白了</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/TMleRgC5CSPIByoDpUaatw">Spring Boot中如何统一API接口响应格式？–HandlerMethodReturnValueHandler</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/fYCnSSe00AOJJi02Rj2z6w">如何优雅记录HTTP请求&#x2F;响应数据？</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/lxpCNGChQUsN9etLONXdoQ">聊聊Spring中最常用的11个扩展点</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/dj9pRBy5EIpJiJHVDuXkIw">16个有用的SpringBoot扩展接口</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/LfIDxs8Pr1GhsSk3vsS5QA">HandlerMethod</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/VCBy5eLlqL6nbIURW8iDDA">Spring MVC中处理Request和Response的策略</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/uJWbW6AREpyN8fG7_r46-w">Spring中的@EventListener注解</a></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<h2 id="springboot-starter"><a href="#springboot-starter" class="headerlink" title="springboot-starter"></a>springboot-starter</h2><p><strong>spring.factory在spring boot3.0移除的解决办法</strong></p>
<p>如果你有探索过这些Starter的原理，那你一定知道Spring Boot并没有消灭这些原本你要配置的Bean，而是将这些Bean做成了一些默认的配置类，同时利用<code>/META-INF/spring.factories</code>这个文件来指定要加载的默认配置。这样当Spring Boot应用启动的时候，就会根据引入的各种Starter中的<code>/META-INF/spring.factories</code>文件所指定的配置类去加载Bean。而Spring Boot 2.7中，有一个不推荐使用的内容就是关于这个<code>/META-INF/spring.factories</code>文件的，在Spring Boot 3开始将移除对<code>/META-INF/spring.factories</code>的支持。<br><img src="https://upload-images.jianshu.io/upload_images/1447174-7f1483a62fe2652f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/937/format/webp" alt="img"></p>
<p>那么具体怎么改呢？下面以之前我们编写的一个swagger的starter为例，它的<code>/META-INF/spring.factories</code>内容是这样的：</p>
<pre class="line-numbers language-none"><code class="language-none">org.springframework.boot.autoconfigure.EnableAutoConfiguration&#x3D;\
  com.spring4all.swagger.SwaggerAutoConfiguration<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>我们只需要创建一个新的文件：<code>/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports</code>，内容的话只需要直接放配置类就可以了，比如这样：</p>
<pre class="line-numbers language-none"><code class="language-none">com.spring4all.swagger.SwaggerAutoConfiguration<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>注意：这里多了一级spring目录。<br>如果你觉得维护这个太麻烦的话，还可以使用mica-auto来让他自动生成，具体怎么用可以看之前发的<a target="_blank" rel="noopener" href="https://links.jianshu.com/go?to=https://blog.didispace.com/spring-factories-mico-auto/">这篇文章</a>。</p>
<p><strong>文章</strong></p>
<table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/SMC1rGh3ALc4YSN0KojJ1Q">如何优雅的实现Spring Boot接口参数加密解密？</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/mdo99iuBpJcTLwpfj-_Csw">SpringBoot接口层统一加密解密</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/5VJcjjjBE-WRbWuq8Wto4g">Spring Boot接口数据加解密技巧</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ASBRANcdMI2VXflyvD6wiA">不管是spring.factories还是最新的imports文件，这个神器帮你全自动生成！</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://github.com/SpringForAll/spring-boot-starter-swagger">spring-boot-starter-swagger</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/RqiDZ83Unp1v6yKbehao8A">图文并茂，Spring Boot Starter万字详解！还有谁不会？</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Mh7ltwytt5K0yeqjbQJi_w">五分钟说清楚Spring Boot的自动配置原理</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/FRI02LzEGhkVW7V7MvDL6A">Spring Boot自动装配原理，这一篇就够了！</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Jh0zv6fkxflWY3IgRL9SvQ">Tomcat在SpringBoot中是如何启动的？</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s?__biz=Mzg2MDYzODI5Nw==&mid=2247493940&idx=1&sn=a76e59fb9574f7fd5332185416c40ced&source=41#wechat_redirect">手把手带你剖析Springboot启动原理！</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/S5LXIKkATark3Mg-ki71Kw">一个注解，搞定SpringBoot自动化启动</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/4-rDXdQBVqLIbdUHPqhu2Q">SpringBoot自动装配的原理分析</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/1zdk-Gqh5JcZaUil-JWCUA">SpringBoot自动装配原理</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/YptVdZYAAmZ7UetGBYeUqg">一文看懂SpringBoot启动流程！</a></td>
<td align="center"></td>
</tr>
</tbody></table>
<h2 id="springboot-state-machine"><a href="#springboot-state-machine" class="headerlink" title="springboot-state-machine"></a>springboot-state-machine</h2><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/bmnCxzwZRqHbAhdEWAHe8A">项目终于用上了Spring状态机，非常优雅！</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://baomidou.com/pages/24112f/">mybatis-plus文档</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://gitee.com/baomidou/mybatis-plus-samples">mybatis-plus-samples</a></th>
</tr>
</thead>
</table>
<h2 id="springboot-upload"><a href="#springboot-upload" class="headerlink" title="springboot-upload"></a>springboot-upload</h2><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/4DZnu3WKRIxwLRObpcazyw">Spring Boot项目超大文件上传时，如何实现秒传？</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/OSX7ya8oZmGPGXrWZ7kMeQ">大文件上传下载实现思路，分片、断点续传代码实现</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/wshoAM5FuTHoecroapiAew">Spring Boot多线程异步上传图片、处理水印、缩略图</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/mXvxFVyxZf-DYFVG8Y8pjg">Spring Boot分片上传、断点续传、大文件极速秒传</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/WdZO8EpN5T4DIuYCmfMU-w">Spring Boot实现文件断点下载，实战来了！</a></td>
<td align="center"></td>
</tr>
</tbody></table>
<h2 id="springboot-websocket"><a href="#springboot-websocket" class="headerlink" title="springboot-websocket"></a>springboot-websocket</h2><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/S-Pqx8mn1c1OvFyiXqmkCQ">WebSocket分布式集群怎么搞？</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Z8gmWEF8XAEVNe2xbFyYeg">聊聊分布式WebSocket集群解决方案</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/kaRx6tevY8oVbfQh5swfjQ">Spring Boot + Netty + WebSocket实现消息推送</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/z4a1DFpZtnd4kwPcPTS7VQ">Spring Boot+Netty+Websocket实现后台向前端推送信息</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/NtzdkexNfmuwdth32Q9lZQ">Spring Boot + Web Socket打造实时监控异常</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/-48eec9vNKyU1cJpCRIwmQ">实现web实时消息推送的方案，7种！</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/7UkyiILNpwPSe4uNWr02pA">Spring Boot整合Socket实战案例,实现单点发送、广播群发，1对1，1对多</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/GEFYyyz7fj0pQ8Mm8JftFQ">基于ChatGPT API和WebSocket的实时机器人聊天应用</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/yBK5UG3Xkr5YMF-0jCbSjA">实现web实时消息推送的方案总结</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/-lCSSYyr1KR4-aP1by22CQ">Spring Boot整合Socket实战案例，实现单点发送、广播群发，1对1，1对多</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/lLCjXKUshXe_LYMjoywwBw">WebSocket的6种集成方式</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/F5bg9bAe3H6hLQ4XLeex4A">万字详解，带你彻底掌握WebSocket用法</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/YLqBw4wnbuKy29bkxRZ1HA">Spring Boot + WebSocket实现实时通知功能</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/R2JTglbbq72ziq-GoSQyug">你还在使用websocket实现实时消息推送吗？(SSE使用)</a></td>
<td align="center"></td>
</tr>
</tbody></table>
<h2 id="SpringBoot"><a href="#SpringBoot" class="headerlink" title="SpringBoot"></a>SpringBoot</h2><h3 id="SpringApplication-run-3-0版本"><a href="#SpringApplication-run-3-0版本" class="headerlink" title="SpringApplication.run()(3.0版本)"></a>SpringApplication.run()(3.0版本)</h3><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// SpringApplication构造方法</span>
<span class="token keyword">new</span> <span class="token class-name">SpringApplication</span><span class="token punctuation">(</span><span class="token class-name">Application</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
    <span class="token keyword">this</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token class-name">ResourceLoader</span><span class="token punctuation">)</span><span class="token keyword">null</span><span class="token punctuation">,</span> primarySources<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>
<span class="token comment">// 创建一个新的实例，这个应用程序的上下文将要从指定的来源加载Bean</span>
<span class="token keyword">public</span> <span class="token class-name">SpringApplication</span><span class="token punctuation">(</span><span class="token class-name">ResourceLoader</span> resourceLoader<span class="token punctuation">,</span> <span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> primarySources<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 资源初始化资源加载器，默认为null</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>resourceLoader <span class="token operator">=</span> resourceLoader<span class="token punctuation">;</span>
    <span class="token comment">// 断言主要加载资源类不能为null，否则报错</span>
    <span class="token class-name">Assert</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span>primarySources<span class="token punctuation">,</span> <span class="token string">"PrimarySources must not be null"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 初始化主要加载资源类集合并去重</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>primarySources <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LinkedHashSet</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token class-name">Arrays</span><span class="token punctuation">.</span><span class="token function">asList</span><span class="token punctuation">(</span>primarySources<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 推断当前 WEB 应用类型，一共有三种：NONE(非web项目),SERVLET(servlet web项目),REACTIVE(响应式web项目)</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>webApplicationType <span class="token operator">=</span> <span class="token class-name">WebApplicationType</span><span class="token punctuation">.</span><span class="token function">deduceFromClasspath</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 设置应用上线文初始化器,从"META-INF/spring.factories"读取ApplicationContextInitializer类的实例名称集合并去重，并进行set去重。（一共7个）</span>
    <span class="token function">setInitializers</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token class-name">Collection</span><span class="token punctuation">)</span> <span class="token function">getSpringFactoriesInstances</span><span class="token punctuation">(</span><span class="token class-name">ApplicationContextInitializer</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 设置监听器,从"META-INF/spring.factories"读取ApplicationListener类的实例名称集合并去重，并进行set去重。（一共11个）</span>
    <span class="token function">setListeners</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token class-name">Collection</span><span class="token punctuation">)</span> <span class="token function">getSpringFactoriesInstances</span><span class="token punctuation">(</span><span class="token class-name">ApplicationListener</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 推断主入口应用类，通过当前调用栈，获取Main方法所在类，并赋值给mainApplicationClass</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>mainApplicationClass <span class="token operator">=</span> <span class="token function">deduceMainApplicationClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

<span class="token comment">// run()方法</span>
<span class="token keyword">public</span> <span class="token class-name">ConfigurableApplicationContext</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> args<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">long</span> startTime <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">nanoTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">DefaultBootstrapContext</span> bootstrapContext <span class="token operator">=</span> <span class="token function">createBootstrapContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">ConfigurableApplicationContext</span> context <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">configureHeadlessProperty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 创建所有spring运行监听器并发布应用启动事件,加载所有SpringApplicationRunListener的实现类</span>
    <span class="token class-name">SpringApplicationRunListeners</span> listeners <span class="token operator">=</span> <span class="token function">getRunListeners</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 调用了starting</span>
    listeners<span class="token punctuation">.</span><span class="token function">starting</span><span class="token punctuation">(</span>bootstrapContext<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>mainApplicationClass<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// 创建ApplicationArguments对象,获取应用程序启动参数</span>
        <span class="token class-name">ApplicationArguments</span> applicationArguments <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DefaultApplicationArguments</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 根据运行监听器和应用参数来准备spring环境,自定义监听器加载配置信息和系统环境变量,调用了environmentPrepared</span>
        <span class="token class-name">ConfigurableEnvironment</span> environment <span class="token operator">=</span> <span class="token function">prepareEnvironment</span><span class="token punctuation">(</span>listeners<span class="token punctuation">,</span> bootstrapContext<span class="token punctuation">,</span> applicationArguments<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">Banner</span> printedBanner <span class="token operator">=</span> <span class="token function">printBanner</span><span class="token punctuation">(</span>environment<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 创建Spring上下文并加载Bean</span>
        context <span class="token operator">=</span> <span class="token function">createApplicationContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        context<span class="token punctuation">.</span><span class="token function">setApplicationStartup</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>applicationStartup<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 准备ApplicationContext,该步骤包含一个非常关键的操作，将启动类注入容器，为后续开启自动化提供基础,内部调用了contextPrepared、contextLoaded</span>
        <span class="token function">prepareContext</span><span class="token punctuation">(</span>bootstrapContext<span class="token punctuation">,</span> context<span class="token punctuation">,</span> environment<span class="token punctuation">,</span> listeners<span class="token punctuation">,</span> applicationArguments<span class="token punctuation">,</span> printedBanner<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 刷新应用上下文（自动装配，初始化IOC容器）</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">refreshContext</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 应用上下文刷新后置处理，做一些扩展功能</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">afterRefresh</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> applicationArguments<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">Duration</span> timeTakenToStartup <span class="token operator">=</span> <span class="token class-name">Duration</span><span class="token punctuation">.</span><span class="token function">ofNanos</span><span class="token punctuation">(</span><span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">nanoTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> startTime<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>logStartupInfo<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">StartupInfoLogger</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>mainApplicationClass<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">logStarted</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getApplicationLog</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> timeTakenToStartup<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
 		<span class="token comment">// 执行所有的Runner运行器,调用了started</span>
        listeners<span class="token punctuation">.</span><span class="token function">started</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> timeTakenToStartup<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 执行所有的Runner运行器</span>
        <span class="token function">callRunners</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> applicationArguments<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> var12<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>var12 <span class="token keyword">instanceof</span> <span class="token class-name">AbandonedRunException</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">throw</span> var12<span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
 		<span class="token comment">// 内部调用了failed</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">handleRunFailure</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> var12<span class="token punctuation">,</span> listeners<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalStateException</span><span class="token punctuation">(</span>var12<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>context<span class="token punctuation">.</span><span class="token function">isRunning</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token class-name">Duration</span> timeTakenToReady <span class="token operator">=</span> <span class="token class-name">Duration</span><span class="token punctuation">.</span><span class="token function">ofNanos</span><span class="token punctuation">(</span><span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">nanoTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> startTime<span class="token punctuation">)</span><span class="token punctuation">;</span>
             <span class="token comment">// 发布应用上下文就绪事件,调用了reday</span>
            listeners<span class="token punctuation">.</span><span class="token function">ready</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> timeTakenToReady<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
		<span class="token comment">// 返回应用上下文</span>
        <span class="token keyword">return</span> context<span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> var11<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>var11 <span class="token keyword">instanceof</span> <span class="token class-name">AbandonedRunException</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">throw</span> var11<span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">handleRunFailure</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> var11<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token class-name">SpringApplicationRunListeners</span><span class="token punctuation">)</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalStateException</span><span class="token punctuation">(</span>var11<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span>

<span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">SpringApplicationRunListener</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// run方法第一次被执行时调用，早期初始化工作</span>
    <span class="token keyword">void</span> <span class="token function">starting</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// environment创建后，ApplicationContext创建前</span>
    <span class="token keyword">void</span> <span class="token function">environmentPrepared</span><span class="token punctuation">(</span><span class="token class-name">ConfigurableEnvironment</span> environment<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// ApplicationContext实例创建，部分属性设置了</span>
    <span class="token keyword">void</span> <span class="token function">contextPrepared</span><span class="token punctuation">(</span><span class="token class-name">ConfigurableApplicationContext</span> context<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// ApplicationContext加载后，refresh前</span>
    <span class="token keyword">void</span> <span class="token function">contextLoaded</span><span class="token punctuation">(</span><span class="token class-name">ConfigurableApplicationContext</span> context<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// refresh后</span>
    <span class="token keyword">void</span> <span class="token function">started</span><span class="token punctuation">(</span><span class="token class-name">ConfigurableApplicationContext</span> context<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 所有初始化完成后，run结束前</span>
    <span class="token keyword">void</span> <span class="token function">running</span><span class="token punctuation">(</span><span class="token class-name">ConfigurableApplicationContext</span> context<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 初始化失败后</span>
    <span class="token keyword">void</span> <span class="token function">failed</span><span class="token punctuation">(</span><span class="token class-name">ConfigurableApplicationContext</span> context<span class="token punctuation">,</span> <span class="token class-name">Throwable</span> exception<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h3 id="Spring-MVC配置相关"><a href="#Spring-MVC配置相关" class="headerlink" title="Spring MVC配置相关"></a>Spring MVC配置相关</h3><p>Spring Boot中，SpringMVC相关的自动化配置是在<code>WebMvcAutoConfiguration</code>配置类中实现的，它的生效条件有一条，就是当不存在WebMvcConfigurationSupport的实例时，这个自动化配置才会生生效。因此，<strong>如果我们在Spring Boot中自定义SpringMVC配置时选择了继承WebMvcConfigurationSupport，就会导致Spring Boot中SpringMVC的自动化配置失效</strong>。Spring Boot给我们提供了很多自动化配置，很多时候当我们修改这些配置的时候，并不是要全盘否定Spring Boot提供的自动化配置，我们可能只是针对某一个配置做出修改，其他的配置还是按照Spring Boot默认的自动化配置来，而继承WebMvcConfigurationSupport来实现对SpringMVC的配置会导致所有的SpringMVC自动化配置失效，因此，一般情况下我们不选择这种方案。</p>
<blockquote>
<p>若直接继承WebMvcConfigurationSupport,会导致application.properties配置文件不生效，需要增加以下等相关配置代替配置文件<br>例:</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Bean</span>
<span class="token keyword">public</span> <span class="token class-name">InternalResourceViewResolver</span> <span class="token function">resourceViewResolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
    <span class="token class-name">InternalResourceViewResolver</span> internalResourceViewResolver <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">InternalResourceViewResolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 请求视图文件的前缀地址</span>
    internalResourceViewResolver<span class="token punctuation">.</span><span class="token function">setPrefix</span><span class="token punctuation">(</span><span class="token string">"/WEB-INF/views/"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 请求视图文件的后缀</span>
    internalResourceViewResolver<span class="token punctuation">.</span><span class="token function">setSuffix</span><span class="token punctuation">(</span><span class="token string">".jsp"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> internalResourceViewResolver<span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>

<span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">configureViewResolvers</span><span class="token punctuation">(</span><span class="token class-name">ViewResolverRegistry</span> registry<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">configureViewResolvers</span><span class="token punctuation">(</span>registry<span class="token punctuation">)</span><span class="token punctuation">;</span>
    registry<span class="token punctuation">.</span><span class="token function">viewResolver</span><span class="token punctuation">(</span><span class="token function">resourceViewResolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    registry<span class="token punctuation">.</span><span class="token function">jsp</span><span class="token punctuation">(</span><span class="token string">"/WEB-INF/views/"</span><span class="token punctuation">,</span><span class="token string">".jsp"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
</blockquote>
<p>在SpringBoot2.0及Spring 5.0<strong>WebMvcConfigurerAdapter已被废弃,标记为过时</strong>。使用方式：</p>
<ol>
<li>直接实现WebMvcConfigurer（官方推荐）<a target="_blank" rel="noopener" href="https://blog.csdn.net/zhangpower1993/article/details/89016503">WebMvcConfigurer详解</a></li>
<li>直接继承WebMvcConfigurationSupport(继承此方法会导致application.properties不生效)</li>
</ol>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// WebMvcConfigurer常用的方法 </span>
<span class="token comment">// 解决跨域问题 </span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">addCorsMappings</span><span class="token punctuation">(</span><span class="token class-name">CorsRegistry</span> registry<span class="token punctuation">)</span> <span class="token punctuation">;</span>
<span class="token comment">// 添加拦截器</span>
<span class="token keyword">void</span> <span class="token function">addInterceptors</span><span class="token punctuation">(</span><span class="token class-name">InterceptorRegistry</span> registry<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 这里配置视图解析器 </span>
<span class="token keyword">void</span> <span class="token function">configureViewResolvers</span><span class="token punctuation">(</span><span class="token class-name">ViewResolverRegistry</span> registry<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 配置内容裁决的一些选项</span>
<span class="token keyword">void</span> <span class="token function">configureContentNegotiation</span><span class="token punctuation">(</span><span class="token class-name">ContentNegotiationConfigurer</span> configurer<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 视图跳转控制器 </span>
<span class="token keyword">void</span> <span class="token function">addViewControllers</span><span class="token punctuation">(</span><span class="token class-name">ViewControllerRegistry</span> registry<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 静态资源处理 </span>
<span class="token keyword">void</span> <span class="token function">addResourceHandlers</span><span class="token punctuation">(</span><span class="token class-name">ResourceHandlerRegistry</span> registry<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 默认静态资源处理器</span>
<span class="token keyword">void</span> <span class="token function">configureDefaultServletHandling</span><span class="token punctuation">(</span><span class="token class-name">DefaultServletHandlerConfigurer</span> configurer<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<blockquote>
<p>跟自定义SpringMVC相关的类和注解主要有如下四个：</p>
<ul>
<li>WebMvcConfigurerAdapter</li>
<li>WebMvcConfigurer</li>
<li>WebMvcConfigurationSupport</li>
<li>@EnableWebMvc</li>
</ul>
<p>Spring Boot1.x中，自定义SpringMVC配置可以通过继承WebMvcConfigurerAdapter来实现。<br>Spring Boot2.x中，自定义SpringMVC配置可以通过实现WebMvcConfigurer接口来完成。<br>如果在Spring Boot中使用继承WebMvcConfigurationSupport来实现自定义SpringMVC配置，或者在Spring Boot中使用了@EnableWebMvc注解，都会导致Spring Boot中默认的SpringMVC自动化配置失效。在纯Java配置的SSM环境中，如果我们要自定义SpringMVC配置，有两种办法，第一种就是直接继承自WebMvcConfigurationSupport来完成SpringMVC配置，还有一种方案就是实现WebMvcConfigurer接口来完成自定义SpringMVC配置，如果使用第二种方式，则需要给SpringMVC的配置类上额外添加@EnableWebMvc注解，表示启用WebMvcConfigurationSupport，这样配置才会生效。换句话说，在纯Java配置的SSM中，如果你需要自定义SpringMVC配置，你离不开WebMvcConfigurationSupport，所以在这种情况下建议通过继承WebMvcConfigurationSupport来实现自动化配置。</p>
</blockquote>
<h3 id="扫描注解"><a href="#扫描注解" class="headerlink" title="扫描注解"></a>扫描注解</h3><ol>
<li>implements SmartInitializingSingleton</li>
</ol>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> beanDefinitionsNames<span class="token operator">=</span>applicationContext<span class="token punctuation">.</span><span class="token function">getBeanNamesForType</span><span class="token punctuation">(</span><span class="token class-name">LifeCycleController</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span><span class="token boolean">false</span><span class="token punctuation">,</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">String</span> beanDefinitionName <span class="token operator">:</span> beanDefinitionsNames<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
	<span class="token class-name">Object</span> bean <span class="token operator">=</span> applicationContext<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>beanDefinitionName<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// map = 注解及注解所在的方法</span>
    <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Method</span><span class="token punctuation">,</span> <span class="token class-name">InvokeMethod</span><span class="token punctuation">></span></span> annotatedMethods <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
    <span class="token keyword">try</span><span class="token punctuation">&#123;</span>
        <span class="token comment">// 查找bean里指定注解的方法</span>
        annotatedMethods <span class="token operator">=</span> <span class="token class-name">MethodIntrospector</span><span class="token punctuation">.</span><span class="token function">selectMethods</span><span class="token punctuation">(</span>bean<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">MethodIntrospector<span class="token punctuation">.</span>MetadataLookup</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">InvokeMethod</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token annotation punctuation">@Override</span>
            <span class="token keyword">public</span> <span class="token class-name">InvokeMethod</span> <span class="token function">inspect</span><span class="token punctuation">(</span><span class="token class-name">Method</span> method<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                <span class="token keyword">return</span> <span class="token class-name">AnnotatedElementUtils</span><span class="token punctuation">.</span><span class="token function">findMergedAnnotation</span><span class="token punctuation">(</span>method<span class="token punctuation">,</span><span class="token class-name">InvokeMethod</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span><span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<ol start="2">
<li>implements BeanPostProcessor</li>
</ol>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Method</span><span class="token punctuation">[</span><span class="token punctuation">]</span> methods <span class="token operator">=</span> <span class="token class-name">ReflectionUtils</span><span class="token punctuation">.</span><span class="token function">getAllDeclaredMethods</span><span class="token punctuation">(</span>bean<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">Method</span> method <span class="token operator">:</span> methods<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token class-name">MsgEvent</span> myMsgEvent <span class="token operator">=</span> <span class="token class-name">AnnotationUtils</span><span class="token punctuation">.</span><span class="token function">findAnnotation</span><span class="token punctuation">(</span>method<span class="token punctuation">,</span> <span class="token class-name">MsgEvent</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>
<ol start="3">
<li><p><code>HandlerMethod.getMethodAnnotation(Class&lt;A&gt; annotationType)</code>,底层<code>AnnotatedElementUtils.findMergedAnnotation(method,InvokeMethod.class)</code>实现</p>
</li>
<li><p>HandlerMethodSelector在spring5.0中废弃</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span> handlerType <span class="token operator">=</span> applicationContext<span class="token punctuation">.</span><span class="token function">getType</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">final</span> <span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span> userType <span class="token operator">=</span> <span class="token class-name">ClassUtils</span><span class="token punctuation">.</span><span class="token function">getUserClass</span><span class="token punctuation">(</span>handlerType<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">Set</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Method</span><span class="token punctuation">></span></span> <span class="token operator">=</span> <span class="token class-name">HandlerMethodSelector</span><span class="token punctuation">.</span><span class="token function">selectMethods</span><span class="token punctuation">(</span>userType<span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">ReflectionUtils<span class="token punctuation">.</span>MethodFilter</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
	<span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token class-name">Method</span> method<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    	<span class="token class-name">OpanApi</span> methodAnnotation <span class="token operator">=</span> <span class="token class-name">AnnotationUtils</span><span class="token punctuation">.</span><span class="token function">findAnnotation</span><span class="token punctuation">(</span>method<span class="token punctuation">,</span> <span class="token class-name">OpanApi</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>methodAnnotation <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li>
</ol>
<h3 id="相关文章"><a href="#相关文章" class="headerlink" title="相关文章"></a>相关文章</h3><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/MX2YxMASHfz4dr3a4sgFcw">Spring Boot核心知识剖析，写得太好了</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/JoEmiVP1lp9OVO7x1-x4zw">为什么SpringBoot的jar可以直接运行？</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/kXHc35ZvdoHAQYZ6pGvAsw">Spring Boot为何可以使用Jar包启动？</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/w1zRHJqayRycIAxmnonrTA">Spring Boot手动配置@Enable的秘密</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE">Spring Boot源码分析(2.1.0.RELEASE)</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://github.com/LandGrey/SpringBootVulExploit">Spring Boot相关漏洞学习资料</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/y1GQ6hrZ3MOEozj25MBknw">springboot源码解析(一)：启动过程</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/eT2A76G-42MvvN6RMZcPMg">3分钟了解SpringBoot的启动流程</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/sjn847TZ814FcKCF3MPBqA">Spring Boot面试：说说自动配置的原理？</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/n0BdIl6KGSuwxMqy77iugg">Spring Boot启动注解分析</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/iprPno-RbOyLPfZiKFfReQ">SpringBoot启动原理详解(图文全面总结)</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/b66It1YGytbbShYSjFSoTA">如何加快Spring Boot启动速度？</a></td>
</tr>
</tbody></table>
<h2 id="springcloud-config"><a href="#springcloud-config" class="headerlink" title="springcloud-config"></a>springcloud-config</h2><p><strong>springcloud-config-server</strong></p>
<p>客户端通过直接调用配置中心的server端来获取配置文件信息。但是存在了一个问题，客户端和服务端的耦合性太高，如果server端要做集群，客户端只能通过原始的方式来路由，server端改变IP地址的时候，客户端也需要修改配置，不符合springcloud服务治理的理念。<br>springcloud提供了这样的解决方案，我们只需要将server端当做一个服务注册到eureka中，client端去eureka中去获取配置中心server端的服务既可。与之前配置相比主要是去掉了spring.cloud.config.uri直接指向server端地址的配置，增加了最后的三个配置：</p>
<ol>
<li>spring.cloud.config.discovery.enabled ：开启Config服务发现支持</li>
<li>spring.cloud.config.discovery.serviceId ：指定server端的name,也就是server端spring.application.name的值</li>
<li>eureka.client.serviceUrl.defaultZone ：指向注册中心的地址</li>
</ol>
<p>config server集成spring-cloud-starter-bus-kafka和spring-boot-starter-actuator<br>在远程仓库修改配置文件的时候通过 <a href="http://config-server:port/actuator/bus-refresh">http://config-server:port/actuator/bus-refresh</a> 来刷新所有springcloud-config-client，无须重启springcloud-config-client,也无须在springcloud-config-client上一个一个单独通过 <a href="http://configclientID:port/actuator/refresh">http://configclientID:port/actuator/refresh</a> 来刷新config-client的数据。<br>借助Git仓库的WebHook，我们就可轻松实现配置的自动刷新</p>
<p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/em0GYlJteARwSowzBSUF_Q">Nacos、Apollo、Config配置中心如何选型</a></p>
<h2 id="springcloud-consul"><a href="#springcloud-consul" class="headerlink" title="springcloud-consul"></a>springcloud-consul</h2><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://www.consul.io/downloads">Consul下载</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://www.consul.io/docs">Consul文档</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://github.com/hashicorp/consul">Consul Github</a></th>
</tr>
</thead>
</table>
<h2 id="springcloud-gateway"><a href="#springcloud-gateway" class="headerlink" title="springcloud-gateway"></a>springcloud-gateway</h2><p><strong>微服务为什么要使用网关</strong></p>
<p>不同的微服务一般会有不同的网络地址，而外部客户端可能需要调用多个服务的接口才能完成一个业务需求，如果让客户端直接与各个微服务通信，会有以下的问题：</p>
<ol>
<li>客户端会多次请求不同的微服务，增加了客户端的复杂性</li>
<li>存在跨域请求，在一定场景下处理相对复杂</li>
<li>认证复杂，每个服务都需要独立认证</li>
<li>难以重构，随着项目的迭代，可能需要重新划分微服务。例如，可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信，那么重构将会很难实施</li>
<li>某些微服务可能使用了防火墙 &#x2F; 浏览器不友好的协议，直接访问会有一定的困难<br>以上这些问题可以借助网关解决。</li>
</ol>
<p><strong>微服务网关的优点</strong></p>
<ol>
<li>安全 ，只有网关系统对外进行暴露，微服务可以隐藏在内网，通过防火墙保护。</li>
<li>易于监控。可以在网关收集监控数据并将其推送到外部系统进行分析。</li>
<li>易于认证。可以在网关上进行认证，然后再将请求转发到后端的微服务，而无须在每个微服务中进行认证。</li>
<li>减少了客户端与各个微服务之间的交互次数</li>
<li>易于统一授权。</li>
<li>网关的作用:转发功能，熔断功能，限流功能</li>
</ol>
<p><strong>文章</strong></p>
<table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/tRSLB1ojWtxN4dXdOH7guA">网关系统就该这么设计（万能通用）</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/IwhfF7CxJMJMw6-WztgcPQ">为什么我们的微服务中需要网关？</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://blog.csdn.net/congge_study/article/details/129892885">Spring Cloud 微服务网关Gateway使用详解</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Xzoxeoy7FgDy4b9NHMView">Spring Cloud Gateway在微服务架构下的最佳实践</a></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<h2 id="springcloud-openfeign"><a href="#springcloud-openfeign" class="headerlink" title="springcloud-openfeign"></a>springcloud-openfeign</h2><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/B0wh-3GNUDotJ_OscivYxA">Feign调用常见问题避坑指南！</a></th>
<th align="center"></th>
<th align="center"></th>
</tr>
</thead>
</table>
<h2 id="springcloud-shenyu"><a href="#springcloud-shenyu" class="headerlink" title="springcloud-shenyu"></a>springcloud-shenyu</h2><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://github.com/apache/shenyu">GitHub ShenYu</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://shenyu.apache.org/zh/docs/index/">Apache ShenYu文档</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://archive.apache.org/dist/incubator/shenyu/">ShenYu Admin\BootStrap二进制包下载地址(不使用源码启动的话直接下载包启动)</a></th>
</tr>
</thead>
</table>
<h2 id="springcloud-tencent"><a href="#springcloud-tencent" class="headerlink" title="springcloud-tencent"></a>springcloud-tencent</h2><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://polarismesh.cn/">北极星官网</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://github.com/polarismesh/polaris/releases">北极星下载地址</a></th>
<th align="center"></th>
</tr>
</thead>
</table>
<h2 id="SpringCloud"><a href="#SpringCloud" class="headerlink" title="SpringCloud"></a>SpringCloud</h2><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://start.spring.io/actuator/info">Spring Cloud与Spring Boot版本对比</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E">Spring Cloud Alibaba版本说明</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86">Spring Cloud Tencent版本管理</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://github.com/spring-cloud">Spring Cloud–GitHub</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://www.springcloud.cc/">Spring Cloud中文网</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/c3fQsf7oNOk9xfA95CtKHw">一份微服务架构手稿图，彻底搞定微服务核心原理！</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/EHPKm50KmHq_KZIHyVef3A">1.5W字搞懂Spring Cloud，太牛了！</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/RoUO3X2uy-WlRzcqTydyRA">微服务三十三问，两万字图文详解！</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/hi0jgVmqoG_ya7xFFwpxAw">把Spring Cloud给拆了！详解每个组件的作用</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/jIP1HTmVMjFVn0MRjKQqdQ">Spring Cloud使用@RefreshScope注解配置动态刷新</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/yPEKKZzlYAzSF_bNZ3Ydsw">Spring Cloud轻松解决跨域，别再乱用了</a></td>
<td align="center"></td>
</tr>
</tbody></table>

                
            </div>
            <hr/>

            

    <div class="reprint" id="reprint-statement">
        
            <div class="reprint__author">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-user">
                        文章作者:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="/blog/about" rel="external nofollow noreferrer">xmxe</a>
                </span>
            </div>
            <div class="reprint__type">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-link">
                        文章链接:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="https://xmxe.github.io/blog/posts/a323428e6f1c/">https://xmxe.github.io/blog/posts/a323428e6f1c/</a>
                </span>
            </div>
            <div class="reprint__notice">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-copyright">
                        版权声明:
                    </i>
                </span>
                <span class="reprint-info">
                    本博客所有文章除特別声明外，均采用
                    <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a>
                    许可协议。转载请注明来源
                    <a href="/blog/about" target="_blank">xmxe</a>
                    !
                </span>
            </div>
        
    </div>

    <script async defer>
      document.addEventListener("copy", function (e) {
        let toastHTML = '<span>复制成功，请遵循本文的转载规则</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">查看</a>';
        M.toast({html: toastHTML})
      });

      function navToReprintStatement() {
        $("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
      }
    </script>



            <div class="tag_share" style="display: block;">
                <div class="post-meta__tag-list" style="display: inline-block;">
                    
                        <div class="article-tag">
                            <span class="chip bg-color">无标签</span>
                        </div>
                    
                </div>
                <div class="post_share" style="zoom: 80%; width: fit-content; display: inline-block; float: right; margin: -0.15rem 0;">
                    <link rel="stylesheet" type="text/css" href="/blog/libs/share/css/share.min.css">
<div id="article-share">

    
    <div class="social-share" data-sites="qq,qzone,wechat,weibo,douban" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>
    <script src="/blog/libs/share/js/social-share.min.js"></script>
    

    

</div>

                </div>
            </div>
            
                <style>
    #reward {
        margin: 40px 0;
        text-align: center;
    }

    #reward .reward-link {
        font-size: 1.4rem;
        line-height: 38px;
    }

    #reward .btn-floating:hover {
        box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2), 0 5px 15px rgba(0, 0, 0, 0.2);
    }

    #rewardModal {
        width: 320px;
        height: 350px;
    }

    #rewardModal .reward-title {
        margin: 15px auto;
        padding-bottom: 5px;
    }

    #rewardModal .modal-content {
        padding: 10px;
    }

    #rewardModal .close {
        position: absolute;
        right: 15px;
        top: 15px;
        color: rgba(0, 0, 0, 0.5);
        font-size: 1.3rem;
        line-height: 20px;
        cursor: pointer;
    }

    #rewardModal .close:hover {
        color: #ef5350;
        transform: scale(1.3);
        -moz-transform:scale(1.3);
        -webkit-transform:scale(1.3);
        -o-transform:scale(1.3);
    }

    #rewardModal .reward-tabs {
        margin: 0 auto;
        width: 210px;
    }

    .reward-tabs .tabs {
        height: 38px;
        margin: 10px auto;
        padding-left: 0;
    }

    .reward-content ul {
        padding-left: 0 !important;
    }

    .reward-tabs .tabs .tab {
        height: 38px;
        line-height: 38px;
    }

    .reward-tabs .tab a {
        color: #fff;
        background-color: #ccc;
    }

    .reward-tabs .tab a:hover {
        background-color: #ccc;
        color: #fff;
    }

    .reward-tabs .wechat-tab .active {
        color: #fff !important;
        background-color: #22AB38 !important;
    }

    .reward-tabs .alipay-tab .active {
        color: #fff !important;
        background-color: #019FE8 !important;
    }

    .reward-tabs .reward-img {
        width: 210px;
        height: 210px;
    }
</style>

<div id="reward">
    <a href="#rewardModal" class="reward-link modal-trigger btn-floating btn-medium waves-effect waves-light red">赏</a>

    <!-- Modal Structure -->
    <div id="rewardModal" class="modal">
        <div class="modal-content">
            <a class="close modal-close"><i class="fas fa-times"></i></a>
            <h4 class="reward-title">你的赏识是我前进的动力</h4>
            <div class="reward-content">
                <div class="reward-tabs">
                    <ul class="tabs row">
                        <li class="tab col s6 alipay-tab waves-effect waves-light"><a href="#alipay">支付宝</a></li>
                        <li class="tab col s6 wechat-tab waves-effect waves-light"><a href="#wechat">微 信</a></li>
                    </ul>
                    <div id="alipay">
                        
                            <img src="/blog/medias/reward/alipay.jpg" class="reward-img" alt="支付宝打赏二维码">
                        
                    </div>
                    <div id="wechat">
                        
                            <img src="/blog/medias/reward/wechat.jpg" class="reward-img" alt="微信打赏二维码">
                        
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    $(function () {
        $('.tabs').tabs();
    });
</script>

            
        </div>
    </div>

    

    

    

    

    

    

    

    

<article id="prenext-posts" class="prev-next articles">
    <div class="row article-row">
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge left-badge text-color">
                <i class="fas fa-chevron-left"></i>&nbsp;上一篇</div>
            <div class="card">
                <a href="/blog/posts/5dd8d96c7458/">
                    <div class="card-image">
                        
                        <img src="https://pic1.zhimg.com/v2-b581c1513e4d3f51a7f281a2243de302.jpg" class="responsive-img" alt="MyBatis相关">
                        
                        <span class="card-title">MyBatis相关</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            缓存一级缓存是默认开启的，它在一个sqlSession会话里面的所有查询操作都会保存到缓存中，一般来说一个请求中的所有增删改查操作都是在同一个sqlSession里面的，所以我们可以认为每个请求都有自己的一级缓存，如果同一个sqlSessi
                        

                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <!--
                            <i class="far fa-clock fa-fw icon-date"></i>2024-05-30
                            -->

                            
                                <i class="fas fa-user fa-fw"></i>
                                <a href="/blog/about" >
                                    xmxe
                                </a>
                            
                        </span>
                        <span class="publish-author">
                            
                                <i class="fas fa-bookmark fa-fw icon-category"></i>
                                
                                    <a href="/blog/categories/Java/" class="post-category">
                                        Java
                                    </a>
                                
                            

                        </span>
                    </div>
                </div>
                
            </div>
        </div>
        
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge right-badge text-color">
                下一篇&nbsp;<i class="fas fa-chevron-right"></i>
            </div>
            <div class="card">
                <a href="/blog/posts/cd3486d816d5/">
                    <div class="card-image">
                        
                        <img src="https://pic1.zhimg.com/70/47807b7f97507fdce8535d03dc5dbd4e_1440w.avis" class="responsive-img" alt="Flash Flash,Hundred Yard Dash">
                        
                        <span class="card-title">Flash Flash,Hundred Yard Dash</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            下载


apache旗下安装包
centos rpm包(cat &#x2F;etc&#x2F;centos-release)
PLSQL历史版本下载



Clash下载 &#x2F; GitHub
Clash Meta for Andr
                        
                        
                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <!--
                            <i class="far fa-clock fa-fw icon-date"></i>2024-05-30
                            -->
                            
                                <i class="fas fa-user fa-fw"></i>
                                <a href="/blog/about" >
                                    xmxe
                                </a>
                            
                        </span>
                        <span class="publish-author">
                            
                                
                                <a href="/blog/tags/%E9%9A%8F%E7%AC%94/">
                                    <span class="chip bg-color">随笔</span>
                                </a>
                                
                            
                            
                        </span>
                    </div>
                </div>
                
            </div>
        </div>
        
    </div>
</article>

</div>



<!-- 代码块功能依赖 -->
<script type="text/javascript" src="/blog/libs/codeBlock/codeBlockFuction.js"></script>

<!-- 代码语言 -->

<script type="text/javascript" src="/blog/libs/codeBlock/codeLang.js"></script>


<!-- 代码块复制 -->

<script type="text/javascript" src="/blog/libs/codeBlock/codeCopy.js"></script>


<!-- 代码块收缩 -->

<script type="text/javascript" src="/blog/libs/codeBlock/codeShrink.js"></script>


    </div>
    <div id="toc-aside" class="expanded col l3 hide-on-med-and-down">
        <div class="toc-widget card">
            <div class="toc-title"><i class="far fa-list-alt"></i>&nbsp;&nbsp;目录</div>
            <div id="toc-content"></div>
        </div>
    </div>
</div>

<!-- TOC 悬浮按钮. -->

<div id="floating-toc-btn" class="hide-on-med-and-down">
    <a class="btn-floating btn-large bg-color">
        <i class="fas fa-list-ul"></i>
    </a>
</div>


<script src="/blog/libs/tocbot/tocbot.min.js"></script>
<script>
    $(function () {
        tocbot.init({
            tocSelector: '#toc-content',
            contentSelector: '#articleContent',
            headingsOffset: -($(window).height() * 0.4 - 45),
            collapseDepth: Number('0'),
            headingSelector: 'h1, h2, h3, h4, h5, h6'
        });

        // modify the toc link href to support Chinese.
        let i = 0;
        let tocHeading = 'toc-heading-';
        $('#toc-content a').each(function () {
            $(this).attr('href', '#' + tocHeading + (++i));
        });

        // modify the heading title id to support Chinese.
        i = 0;
        $('#articleContent').children('h1, h2, h3, h4, h5, h6').each(function () {
            $(this).attr('id', tocHeading + (++i));
        });

        // Set scroll toc fixed.
        let tocHeight = parseInt($(window).height() * 0.4 - 64);
        let $tocWidget = $('.toc-widget');
        $(window).scroll(function () {
            let scroll = $(window).scrollTop();
            /* add post toc fixed. */
            if (scroll > tocHeight) {
                $tocWidget.addClass('toc-fixed');
            } else {
                $tocWidget.removeClass('toc-fixed');
            }
        });

        
        /* 修复文章卡片 div 的宽度. */
        let fixPostCardWidth = function (srcId, targetId) {
            let srcDiv = $('#' + srcId);
            if (srcDiv.length === 0) {
                return;
            }

            let w = srcDiv.width();
            if (w >= 450) {
                w = w + 21;
            } else if (w >= 350 && w < 450) {
                w = w + 18;
            } else if (w >= 300 && w < 350) {
                w = w + 16;
            } else {
                w = w + 14;
            }
            $('#' + targetId).width(w);
        };

        // 切换TOC目录展开收缩的相关操作.
        const expandedClass = 'expanded';
        let $tocAside = $('#toc-aside');
        let $mainContent = $('#main-content');
        $('#floating-toc-btn .btn-floating').click(function () {
            if ($tocAside.hasClass(expandedClass)) {
                $tocAside.removeClass(expandedClass).hide();
                $mainContent.removeClass('l9');
            } else {
                $tocAside.addClass(expandedClass).show();
                $mainContent.addClass('l9');
            }
            fixPostCardWidth('artDetail', 'prenext-posts');
        });
        
    });
</script>

    

</main>




    <footer class="page-footer bg-color">
    
        <link rel="stylesheet" href="/blog/libs/aplayer/APlayer.min.css">
<style>
    .aplayer .aplayer-lrc p {
        
        display: none;
        
        font-size: 12px;
        font-weight: 700;
        line-height: 16px !important;
    }

    .aplayer .aplayer-lrc p.aplayer-lrc-current {
        
        display: none;
        
        font-size: 15px;
        color: #42b983;
    }

    
    .aplayer.aplayer-fixed.aplayer-narrow .aplayer-body {
        left: -66px !important;
    }

    .aplayer.aplayer-fixed.aplayer-narrow .aplayer-body:hover {
        left: 0px !important;
    }

    
</style>
<div class="">
    
    <div class="row">
        <meting-js class="col l8 offset-l2 m10 offset-m1 s12"
                   server="netease"
                   type="song"
                   id="569200213"
                   fixed='true'
                   autoplay='false'
                   theme='#42b983'
                   loop='all'
                   order='random'
                   preload='auto'
                   volume='0.7'
                   list-folded='true'
        >
        </meting-js>
    </div>
</div>

<script src="/blog/libs/aplayer/APlayer.min.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js"></script> -->

    
    <div class="container row center-align" style="margin-bottom: 0px !important;">
        <div class="col s12 m8 l8 copy-right">
            Copyright&nbsp;&copy;
            
            
                <span id="year">2022-2025
                </span>
            
            

            <a href="/blog/about" target="_blank">
                xmxe
            </a>
            |&nbsp;Powered by&nbsp;
            <a href="https://hexo.io/" target="_blank">Hexo</a>
            |&nbsp;Theme&nbsp;
            <a href="https://github.com/blinkfox/hexo-theme-matery" target="_blank">Matery</a>
            <br>

            
            
            
            
            
            

            
            <br>

            
            <br>

            
        </div>
        <div class="col s12 m4 l4 social-link2 ">
    <a href="https://github.com/xmxe" class="tooltipped" target="_blank" data-tooltip="GitHub" data-position="top" data-delay="50">
        <i class="fab fa-github"></i>
    </a>



    <a href="https://gitee.com/xmxe" class="tooltipped" target="_blank" data-tooltip="码云" data-position="top" data-delay="50">
        <svg width="19" height="19" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" style="position: relative; top: 2px; left: 0.5px;">
            <path d="M512 1024C230.4 1024 0 793.6 0 512S230.4 0 512 0s512 230.4 512 512-230.4 512-512 512z m259.2-569.6H480c-12.8 0-25.6 12.8-25.6 25.6v64c0 12.8 12.8 25.6 25.6 25.6h176c12.8 0 25.6 12.8 25.6 25.6v12.8c0 41.6-35.2 76.8-76.8 76.8h-240c-12.8 0-25.6-12.8-25.6-25.6V416c0-41.6 35.2-76.8 76.8-76.8h355.2c12.8 0 25.6-12.8 25.6-25.6v-64c0-12.8-12.8-25.6-25.6-25.6H416c-105.6 0-188.8 86.4-188.8 188.8V768c0 12.8 12.8 25.6 25.6 25.6h374.4c92.8 0 169.6-76.8 169.6-169.6v-144c0-12.8-12.8-25.6-25.6-25.6z" fill="#fff">
            </path>
        </svg>
    </a>

















    
        
          <a href="/blog/download" class="tooltipped" target="_blank" data-tooltip="下载" data-position="top" data-delay="50">
            <i class="fas fa-download"></i>
          </a>
        
    



    <style>
  .mobiledevice {
    display: none !important;
  }

  footer .wechat_qrcode {
    position: fixed;
  }

  /*微信二维码*/
  .wechat_qrcode {
    position: absolute;
    margin-left: 10px;
    bottom: 10px;
    background: url("/blog/medias/xcx.png");
    zoom:40%;
  }

  .wechat:hover .wechat_qrcode {
    width: 430px;
    height: 430px;
    animation: move 0.4s linear 1 normal;
  }

  @keyframes move {
    0% {
      transform: translate(100px, 0);
      opacity: 0;
    }
    50% {
      transform: translate(50px, 0);
      opacity: 0.5;
    }
    100% {
      transform: translate(0, 0);
      opacity: 1;
    }
  }

  @media only screen and (max-width: 601px) {
    .wechat {
      display: none !important;
    }
    .mobiledevice {
      display: inline-block !important;
    }
  }
</style>

<a href="javascript:;" class="wechat" data-position="top" data-delay="50">
  <i class="fab fa-weixin"></i>
  <img class="wechat_qrcode" />
</a>

<a
  href="javascript:;"
  class="tooltipped mobiledevice"
  data-tooltip="微信: 464817304"
  data-position="top"
  data-delay="50"
>
  <i class="fab fa-weixin"></i>
</a>

</div>
    </div>
</footer>

<div class="progress-bar"></div>


    <!-- 搜索遮罩框 -->
<div id="searchModal" class="modal">
    <div class="modal-content">
        <div class="search-header">
            <span class="title"><i class="fas fa-search"></i>&nbsp;&nbsp;搜索</span>
            <input type="search" id="searchInput" name="s" placeholder="请输入搜索的关键字"
                   class="search-input">
        </div>
        <div id="searchResult"></div>
    </div>
</div>

<script type="text/javascript">
$(function () {
    var searchFunc = function (path, search_id, content_id) {
        'use strict';
        $.ajax({
            url: path,
            dataType: "xml",
            success: function (xmlResponse) {
                // get the contents from search data
                var datas = $("entry", xmlResponse).map(function () {
                    return {
                        title: $("title", this).text(),
                        content: $("content", this).text(),
                        url: $("url", this).text()
                    };
                }).get();
                var $input = document.getElementById(search_id);
                var $resultContent = document.getElementById(content_id);
                $input.addEventListener('input', function () {
                    var str = '<ul class=\"search-result-list\">';
                    var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
                    $resultContent.innerHTML = "";
                    if (this.value.trim().length <= 0) {
                        return;
                    }
                    // perform local searching
                    datas.forEach(function (data) {
                        var isMatch = true;
                        var data_title = data.title.trim().toLowerCase();
                        var data_content = data.content.trim().replace(/<[^>]+>/g, "").toLowerCase();
                        var data_url = data.url;
                        data_url = data_url.indexOf('/') === 0 ? data.url : '/' + data_url;
                        var index_title = -1;
                        var index_content = -1;
                        var first_occur = -1;
                        // only match artiles with not empty titles and contents
                        if (data_title !== '' && data_content !== '') {
                            keywords.forEach(function (keyword, i) {
                                index_title = data_title.indexOf(keyword);
                                index_content = data_content.indexOf(keyword);
                                if (index_title < 0 && index_content < 0) {
                                    isMatch = false;
                                } else {
                                    if (index_content < 0) {
                                        index_content = 0;
                                    }
                                    if (i === 0) {
                                        first_occur = index_content;
                                    }
                                }
                            });
                        }
                        // show search results
                        if (isMatch) {
                            str += "<li><a href='" + data_url + "' class='search-result-title'>" + data_title + "</a>";
                            var content = data.content.trim().replace(/<[^>]+>/g, "");
                            if (first_occur >= 0) {
                                // cut out 100 characters
                                var start = first_occur - 20;
                                var end = first_occur + 80;
                                if (start < 0) {
                                    start = 0;
                                }
                                if (start === 0) {
                                    end = 100;
                                }
                                if (end > content.length) {
                                    end = content.length;
                                }
                                var match_content = content.substr(start, end);
                                // highlight all keywords
                                keywords.forEach(function (keyword) {
                                    var regS = new RegExp(keyword, "gi");
                                    match_content = match_content.replace(regS, "<em class=\"search-keyword\">" + keyword + "</em>");
                                });

                                str += "<p class=\"search-result\">" + match_content + "...</p>"
                            }
                            str += "</li>";
                        }
                    });
                    str += "</ul>";
                    $resultContent.innerHTML = str;
                });
            }
        });
    };

    searchFunc('/blog/search.xml', 'searchInput', 'searchResult');
});
</script>

    <!-- 回到顶部按钮 -->
<div id="backTop" class="top-scroll">
    <a class="btn-floating btn-large waves-effect waves-light" href="#!">
        <i class="fas fa-arrow-up"></i>
    </a>
</div>

    <div class="stars-con">
  <div id="stars"></div>
  <div id="stars2"></div>
  <div id="stars3"></div>  
</div>

<!-- 白天和黑夜主题 -->



<script>
  function switchNightMode() {
    
      setTimeout(function () {
        $('body').hasClass('DarkMode') 
        ? ($('body').removeClass('DarkMode'), localStorage.setItem('isDark', '0'), $('#sum-moon-icon').removeClass("fa-sun").addClass('fa-moon')) 
        : ($('body').addClass('DarkMode'), localStorage.setItem('isDark', '1'), $('#sum-moon-icon').addClass("fa-sun").removeClass('fa-moon')),
          
        setTimeout(function () {
          $('.Cuteen_DarkSky').fadeOut(1e3, function () {
            $(this).remove()
          })
        }, 2e3)
      })
  }
</script>
    
    
    <script>
        /* 模式判断 */
        if (localStorage.getItem('isDark') === '1') {
            document.body.classList.add('DarkMode');
            $('#sum-moon-icon').addClass("fa-sun").removeClass('fa-moon')
        } else {
            document.body.classList.remove('DarkMode');
            $('#sum-moon-icon').removeClass("fa-sun").addClass('fa-moon')
        }
    </script>

    <script src="/blog/libs/materialize/materialize.min.js"></script>
    <script src="/blog/libs/masonry/masonry.pkgd.min.js"></script>
    <script src="/blog/libs/aos/aos.js"></script>
    <script src="/blog/libs/scrollprogress/scrollProgress.min.js"></script>
    <script src="/blog/libs/lightGallery/js/lightgallery-all.min.js"></script>
    <script src="/blog/js/matery.js"></script>

    <!-- Baidu Analytics -->

    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    

    

    

	
    

    

    
    <script type="text/javascript" src="/blog/libs/background/ribbon-dynamic.js" async="async"></script>
    

    

    <!-- 冒泡 -->
    
    <script type="text/javascript">
        // 只在桌面版网页启用特效
        // var windowWidth = $(window).width();
        
            document.write('<script type="text/javascript" src="/blog/libs/others/bubleAll.js"><\/script>');
        
        
    </script>
    

    <!-- 弹出文字 -->
    

</body>

</html>
